summaryrefslogtreecommitdiffstats
path: root/doc/developer
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--doc/developer/autotools.md179
-rw-r--r--doc/developer/coding-methods.rst242
-rw-r--r--doc/developer/contributing.rst127
-rw-r--r--doc/developer/module_interface.rst186
-rw-r--r--doc/developer/release-method.rst42
5 files changed, 776 insertions, 0 deletions
diff --git a/doc/developer/autotools.md b/doc/developer/autotools.md
new file mode 100644
index 0000000..a7ad6a8
--- /dev/null
+++ b/doc/developer/autotools.md
@@ -0,0 +1,179 @@
+FreeRADIUS use of GNU autotools
+===============================
+
+The full autotools suite includes many utilities, which we do not
+need or want to use. Especially libtool, for which we use the
+faster replacement, jlibtool.
+
+In a normal autotools setup, one would run "autoreconf" to rebuild
+all of the configure scripts, which will perform at least the
+following tasks:
+
+ - aclocal
+ - autoconf
+ - autoheader
+ - automake
+ - libtoolize
+
+Specifically, all we really want to run is `autoconf`, to rebuild
+the configure scripts.
+
+We have a more complicated setup than most. There is normally just
+one `configure` script, in the top-level directory. In FreeRADIUS
+there are also configure scripts in most RLM module directories as
+well. Autotools is not really set up to handle this well,
+preferring to treat every sub-directory as a separate project.
+
+This means that e.g. cache files are not shared, and include files
+(for configure macros) are not found as they are expected to be in
+the current directory.
+
+What's more, autoconf macros can be found in multiple places - the
+automake install directory, the system aclocal directory, and in
+multiple places in the FreeRADIUS source (mainly `m4/`, but also
+`acinclude.m4`, both potentially in multiple places).
+
+In our setup we want to run the following only:
+
+ - autoconf, to generate configure files and `all.mk` make files.
+ - autoheader, to generate header files.
+
+
+autoconf
+--------
+
+`autoconf` expands a `configure.ac` file to create a `configure`
+script, with optionally also a Makefile. We generate a makefile
+called `all.mk` to work with the boilermake system.
+
+Being based on m4, autoconf needs to find macro definitions from
+somewhere, which will be expanded as needed upon invocation.
+autoconf has several search paths for macros, including some
+system paths for its own internal macros.
+
+Notably within the project, autoconf looks in `aclocal.m4` to find
+"local" macros to add. These days, `aclocal.m4` is supposed to be
+written by the `aclocal` script, so autotools added the concept of
+`acinclude.m4` to put local macros. `aclocal` will add an include
+directive at the bottom of `aclocal.m4` to include the
+`acinclude.m4` file, if it is found in the current directory.
+
+When `aclocal` is run it will scan `configure.ac` for anything
+that looks like a macro to expand. It will then search project
+directories, the automake system directory and the aclocal system
+directory, to find any macros that match. These are copied into
+the `aclocal.m4` file. `autoconf` will then pick up these macro
+definitions and use them when expanding `configure.ac`. Notably,
+macros can be in `*.m4` files in given search directories and
+`aclocal` will extract the macros and copy them over.
+
+`autoconf` itself will not look in `*.m4` files, only in
+`aclocal.m4` and, if that is not found, `acinclude.m4`.
+
+We therefore have, _within one level directory_:
+
+ - `acinclude.m4`, local macro definitions;
+ - `aclocal.m4`, macros collated by `aclocal`;
+ - `m4/` or other directories, macro files searched by `aclocal`;
+ - `configure.ac` the input configure script;
+ - `all.mk`, `configure`, etc as outputs from `autoconf`.
+
+The GNU Autotools manual these days recommends splitting macros
+up, one file per macro, and putting them in the `m4/` directory
+rather than in the `acinclude.m4` file. This makes them much
+easier to maintain.
+
+
+FreeRADIUS sub-directories
+--------------------------
+
+All the above is not too much of an issue for the top-level
+configure script. We can have an automatically generated
+`aclocal.m4` file, macros in `m4/` and extra components in
+`acinclude.m4` if needed. However, the sub-directory configure
+scripts really want to be kept as small as possible. There is no
+real need for a separate `aclocal.m4` file if all of the configure
+scripts could be scanned together. The top-level `m4/` directory and
+`acinclude.m4` file can be used.
+
+Unfortunately, autotools doesn't like to work like this. It wants
+all files to be in one directory, and `aclocal` won't scan more
+than one `configure.ac` file.
+
+The two compromise solutions seem to be:
+
+ - Don't use `aclocal`.
+
+ - Nearly all local macros are put in the top-level
+ `acinclude.m4` file.
+ - A few local macros can go in `m4/`, but they have to
+ explicitly included in configure.ac scripts with
+ `m4_include()`.
+ - `autoconf` has to be run with multiple `-I` include args to
+ capture all the places where macros could be.
+ - Any missing macros won't get pulled in from system
+ locations, because `aclocal` noramally does that.
+ - Sub-directories are relatively clean, e.g. no `aclocal.m4`
+ or `acinclude.m4` files all over the place.
+
+ - Use `aclocal`.
+
+ - The `-I` arg can be passed to `aclocal` which makes it
+ search multiple project directories for local macros to copy
+ to `aclocal.m4`.
+ - All directories with a `configure` script must have an
+ `aclocal.m4` file to collate macros from the top-level `m4/`
+ directory.
+ - The top-level `acinclude.m4` file is ignored except in the
+ top-level configure script, meaning it needs to be symlinked
+ or copied everywhere else.
+ - If `autoconf` finds an `aclocal.m4` file it no longer seems
+ to look for macros elsewhere.
+ - Sub-directories get messy with `aclocal.m4` and
+ `acinclude.m4` files, though these don't need to be checked
+ into the repository.
+ - The top-level `m4/` directory can contain all macros as
+ separate files, which is much cleaner than `acinclude.m4`.
+ - System macros will be found and used.
+
+We pretty much need to use `aclocal` - it removes the need for an
+`acinclude.m4` file (tidier), picks up macros from `m4/`
+automatically (tidier), removes the need for `m4_include()` macros
+(tidier), and means that macros will be found that might not be
+shipped in the FreeRADIUS distribution (easier).
+
+That comes with some downsides as above - we will end up with
+`aclocal.m4` files all over the place, and have to handle the case
+where things were originally in `acinclude.m4` and are not macros.
+
+Fixing `aclocal.m4` files can be done by either including them in
+git (unnecessary) or hiding them with `.gitignore` (best).
+
+Picking up non-macro definitions from `acinclude.m4` can be done
+by adding a new macro, `FR_INIT()`, which defines anything needed.
+In fact, as long as that macro is included, the _entire_
+`m4/fr_init.m4` file will be included by `aclocal`. This means the
+extra definition doesn't even need to be inside the macro.
+
+
+Rebuilding the configure scripts
+================================
+
+The normal way to rebuild all of the autotools outputs is to run
+`autoreconf`. This must not be run with FreeRADIUS as it will
+initialise and use libtool and other things we do not want.
+
+Instead, we have a make target to rebuild everything needed.
+
+ make reconfig
+
+This will rebuild any configure files that are out of date.
+However, sometimes everything needs to be forced, e.g. due to some
+macros changing that are missed by the Make dependencies (maybe
+from the system directories). In this case a forced rebuild can be
+undertaken with:
+
+ find . -name configure.ac | xargs touch
+ make reconfig
+
+This will ensure that _all_ configure scripts are rebuilt.
diff --git a/doc/developer/coding-methods.rst b/doc/developer/coding-methods.rst
new file mode 100644
index 0000000..444696d
--- /dev/null
+++ b/doc/developer/coding-methods.rst
@@ -0,0 +1,242 @@
+Helpful coding methods
+======================
+
+The following is a short set of guidelines to follow while
+programming. It does not address coding styles, function naming
+methods, or debugging methods. Rather, it describes the processes
+which SHOULD go on in the programmers mind, while he is programming.
+
+Coding standards apply to function names, the look of the code, and
+coding consistency. Coding methods apply to the daily practices used
+by the programmer to write code.
+
+
+
+1. Comment your code.
+
+ If you don't, you'll be forced to debug it 6 months later, when
+ you have no clue as to what it's doing.
+
+ If someone REALLY hates you, you'll be forced to debug
+ un-commented code that someone else wrote. You don't want to do
+ that.
+
+ For FreeRADIUS use doxygen @style comments so you get the benefits
+ of docs.freeradius.org.
+
+2. Give things reasonable names.
+
+ Variables and functions should have names. Calling them 'x',
+ 'xx', and 'xxx' makes your life hell. Even 'foo' and 'i' are
+ problematic.
+
+ Avoid smurfs. Don't re-use struct names in field names i.e.
+ struct smurf {
+ char *smurf_pappa_smurf;
+ }
+
+ If your code reads as full english sentences, you're doing it
+ right.
+
+
+3. Check input parameters in the functions you write.
+
+ Your function CANNOT do anything right if the user passed in
+ garbage, and you were too lazy to check for garbage input.
+
+ assert() (rad_assert()) is ugly. Use it.
+
+ GIGO is wrong. If your function gets garbage input, it
+ should complain loudly and with great descriptiveness.
+
+
+4. Write useful error messages.
+
+ "Function failed" is useless as an error message. It makes
+ debugging the code impossible without source-level instrumentation.
+
+ If you're going to instrument the code at source level for error
+ messages, leave the error messages there, so the next sucker won't
+ have to do the same work all over again.
+
+
+5. Check error conditions from the functions you call.
+
+ Your function CANNOT do anything right if you called another
+ function, and they gave you garbage output.
+
+ One of the most common mistakes is::
+
+ fp = fopen(...);
+ fgetc(fp); /* core dumps! */
+
+ If the programmer had bothered to check for a NULL fp (error
+ condition), then he could have produced a DESCRIPTIVE error
+ message, instead of having his program core dump.
+
+
+6. Core dumps are for weenies.
+
+ If your program core dumps accidentally, you're a bad programmer.
+ You don't know what your program is doing, or what it's supposed
+ to be doing when anything goes wrong.
+
+ If it hits an assert() and calls abort(), you're a genius. You've
+ thought ahead to what MIGHT go wrong, and put in an assertion to
+ ensure that it fails in a KNOWN MANNER when something DOES go
+ wrong. (As it usually does...)
+
+
+7. Initialize your variables.
+
+ memset() (talloc_zero()) is your friend. 'ptr = NULL' is
+ nice, too.
+
+ Having variables containing garbage values makes it easy for the
+ code to do garbage things. The contents of local variables are
+ inputs to your function. See #3.
+
+ It's also nearly impossible for you to debug any problems, as you
+ can't tell the variables with garbage values from the real ones.
+
+
+8. Don't allow buffer over-runs.
+
+ They're usually accidental, but they cause core dumps.
+ strcpy() and strcat() are ugly. Use them under duress.
+
+ sizeof() is your friend.
+
+
+9. 'const' is your friend.
+
+ If you don't mean to modify an input structure to your function,
+ declare it 'const'. Declare string constants 'const'. It can't
+ hurt, and it allows more errors to be found at compile time.
+
+ Use 'const' everywhere. Once you throw a few into your code, and
+ have it save you from stupid bugs, you'll blindly throw in 'const'
+ everywhere. It's a life-saver.
+
+
+10. Use C compiler warnings.
+
+ Turn on all of the C compiler warnings possible. You might have
+ to turn some off due to broken system header files, though. But
+ the more warnings the merrier.
+
+ Getting error messages at compile time is much preferable to
+ getting core dumps at run time. See #7.
+
+ Notice that the C compiler error messages are helpful? You should
+ write error messages like this, too. See #4.
+
+
+11. Avoid UNIXisms and ASCIIisms and visualisms.
+
+ You don't know under what system someone will try to run your code.
+ Don't demand that others use the same OS or character set as you use.
+
+ Never assign numbers to pointers. If foo is a char*, and you want it
+ to be be null, assign NULL, not 0. The zeroth location is perfectly
+ as addressable as any other on plenty of OSes. Not all the world
+ runs on Unix (though it should :) ).
+
+ Another common mistake is to assume that the zeroth character in the
+ character set is the string terminator. Instead of terminating a
+ string with 0, use '\0', which is always right. Similarly, memset()
+ with the appropriate value: NULL, '\0', or 0 for pointers, chars,
+ and numbers.
+
+ Don't put tabs in string constants, either. Always use '\t' to
+ represent a tab, instead of ASCII 9. Literal tabs are presented to
+ readers of your code as arbitrary whitespace, and it's easy to mess
+ up.
+
+
+12. Make conditionals explicit.
+
+ Though it's legal to test "if (foo){}", if you test against the
+ appropriate value (like NULL or '\0'), your code is prettier and
+ easier for others to read without having to eyeball your prototypes
+ continuously to figure out what you're doing (especially if your
+ variables aren't well-named). See #2.
+
+
+13. Test your code.
+
+ Even Donald Knuth writes buggy code. You'll never find all of the
+ bugs in your code unless you write a test program for it.
+
+ This also means that you'll have to write your code so that it
+ will be easily testable. As a result, it will look better, and be
+ easier to debug.
+
+Hints, Tips, and Tricks
+-----------------------
+
+This section lists many of the common "rules" associated with code
+submitted to the project. There are always exceptions... but you must
+have a really good reason for doing so.
+
+ 1. Read the Documentation and follow the CodingStyle
+
+ The FreeRADIUS server has a common coding style. Use real tabs
+ to indent. There is whitespace in variable assignments.
+ (i = 1, NOT i=1).
+
+ When in doubt, format your code to look the same as code already
+ in the server. If your code deviates too much from the current
+ style, it is likely to be rejected without further review, and
+ without comment.
+
+ 2. #ifdefs are ugly
+
+ Code cluttered with ifdefs is difficult to read and
+ maintain. Don't do it. Instead, put your ifdefs in a header, and
+ conditionally define 'static inline' functions, or macros, which
+ are used in the code. Let the compiler optimize away the "no-op"
+ case.
+
+ Simple example, of poor code::
+
+ #ifdef CONFIG_MY_FUNKINESS
+ init_my_stuff(foo);
+ #endif
+
+ Cleaned-up example:
+
+ (in header)::
+
+ #ifndef CONFIG_MY_FUNKINESS
+ static inline void init_my_stuff(char *foo) {}
+ #endif
+
+ (in the code itself)::
+
+ init_my_stuff(dev);
+
+ 3. 'static inline' is better than a macro
+
+ Static inline functions are greatly preferred over macros. They
+ provide type safety, have no length limitations, no formatting
+ limitations, and under gcc they are as cheap as macros.
+
+ Macros should only be used for cases where a static inline is
+ clearly suboptimal [there a few, isolated cases of this in fast
+ paths], or where it is impossible to use a static inline
+ function [such as string-izing].
+
+ 'static inline' is preferred over 'static __inline__', 'extern
+ inline', and 'extern __inline__'.
+
+ 4. Don't over-design.
+
+ Don't try to anticipate nebulous future cases which may or may
+ not be useful: "Make it as simple as you can, and no simpler"
+
+ Split up functionality as much as possible. If your code needs
+ to do two unrelated things, write two functions. Mashing two
+ kinds of work into one function makes the server difficult to
+ debug and maintain.
+
diff --git a/doc/developer/contributing.rst b/doc/developer/contributing.rst
new file mode 100644
index 0000000..b40f98c
--- /dev/null
+++ b/doc/developer/contributing.rst
@@ -0,0 +1,127 @@
+Submitting patches or diff's to the FreeRADIUS project
+======================================================
+
+For a person or company wishing to submit a change to the FreeRADIUS project
+the process can sometimes be daunting if you're not familiar with "the system."
+This text is a collection of suggestions which can greatly increase the chances
+of your change being accepted.
+
+Note: Only trivial patches will be accepted via email. Large patches, or
+patches that modify a number of files MUST be submitted as a pull-request via
+GitHub.
+
+Hints and tips
+--------------
+
+1. Describe your changes
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Describe the technical detail of the change(s) your patch or commit includes.
+
+Be as specific as possible. The WORST descriptions possible include things like
+"update file X", "bug fix for file X", or "this patch includes updates for
+subsystem X. Please apply."
+
+If your description starts to get long, that's a sign that you probably need to
+split up your commit. See #3, next.
+
+2. Separate your changes
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Separate each logical change into its own commit.
+
+For example, if your changes include both bug fixes and performance
+enhancements for a single module, separate those changes into two or more
+patches.
+
+On the other hand, if you make a single change to numerous files, group those
+changes into a single commit. Thus a single LOGICAL change is contained within
+a single commit.
+
+If one commit depends on another commit in order for a change to be complete,
+that is OK. Simply note "this commit depends on commit X" in the extended
+commit description.
+
+If your commit includes significant whitespace changes these should also be
+broken out into another, separate, commit.
+
+Submitting patches via GitHub
+-----------------------------
+
+See the following links for more details about submitting via github:
+
+- https://help.github.com/articles/fork-a-repo
+- http://wiki.freeradius.org/contributing/GitHub
+
+Submitting patches via email
+----------------------------
+
+1. "diff -u"
+~~~~~~~~~~~~
+Use ``diff -u`` or ``diff -urN`` to create patches.
+
+All changes to the source occur in the form of patches, as generated by
+diff(1). When creating your patch, make sure to create it in "unified diff"
+format, as supplied by the '-u' argument to diff(1). Patches should be based in
+the root source directory, not in any lower subdirectory.
+
+To create a patch for a single file, it is often sufficient to do::
+
+ SRCTREE=/home/user/src/freeradiusd/
+ MYFILE=src/modules/rlm_foo/foo.c
+
+ cd $SRCTREE
+ cp $MYFILE $MYFILE.orig
+ vi $MYFILE # make your change
+ diff -u $MYFILE.orig $MYFILE > /tmp/patch
+
+To create a patch for multiple files, you should unpack a "vanilla", or
+unmodified source tree, and generate a diff against your own source tree. For
+example::
+
+ MYSRC=/home/user/src/freeradiusd-feature/
+
+ gunzip freeradiusd-version.tar.gz
+ tar xvf freeradiusd-version.tar
+ diff -urN freeradiusd-version $MYSRC > ~/feature-version.patch
+
+
+2. Select e-mail destination
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are on the developers mailing list, send the patch there.
+freeradius-devel@lists.freeradius.org
+
+Otherwise, send the patch to 'patches@freeradius.org'
+
+3. No MIME, no links, no compression, no attachments. Just plain text
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The developers need to be able to read and comment on the changes you are
+submitting. It is important for a developer to be able to "quote" your changes,
+using standard e-mail tools, so that they may comment on specific portions of
+your code.
+
+For this reason, all patches should be submitting e-mail "inline".
+
+Do not attach the patch as a MIME attachment, compressed or not. Many popular
+e-mail applications will not always transmit a MIME attachment as plain text,
+making it impossible to comment on your code. A MIME attachment also takes a
+bit more time to process, decreasing the likelihood of your MIME-attached
+change being accepted.
+
+Compressed patches are generally rejected outright. If the developer has to do
+additional work to read your patch, the odds are that it will be ignored
+completely.
+
+4. E-mail size
+~~~~~~~~~~~~~~
+
+Large changes are not appropriate for mailing lists, and some maintainers. If
+your patch, exceeds 5Kb in size, you must submit the patch via GitHub instead.
+
+5. Name the version of the server
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is important to note, either in the subject line or in the patch
+description, the server version to which this patch applies.
diff --git a/doc/developer/module_interface.rst b/doc/developer/module_interface.rst
new file mode 100644
index 0000000..2fc510e
--- /dev/null
+++ b/doc/developer/module_interface.rst
@@ -0,0 +1,186 @@
+
+RLM Module Interface (for developers)
+=====================================
+
+Overview
+--------
+
+Intent of the server
+^^^^^^^^^^^^^^^^^^^^
+
+FreeRADIUS is an authentication server. It does RADIUS authorization,
+authentication, and accounting. It does NOT do database management,
+user configuration updates, or email. All of those functions may be
+more easily (and correctly) performed in programs outside of the
+server.
+
+The only functionality performed by the server is:
+
+- receive a RADIUS request
+ - process the request
+ - look up information one or more databases
+- store information in one or more databases (proxying can be viewed this way)
+- respond to the request
+
+There is no room, or need, for timers, listening on sockets, or
+anything else in the server. Adding those functions to the server
+means that it will become more complex, more unstable, more insecure,
+and more difficult to maintain.
+
+
+Intent of the modules
+^^^^^^^^^^^^^^^^^^^^^
+
+The intent of modules is that they do small, simple, well-defined
+things when RADIUS packets are received. If the module does things
+when RADIUS packets are NOT received, then it has no business being in
+the server. Similarly, the module infrastructure is NOT intended to
+allow servers, applications, timed events, or anything other than
+handling incoming RADIUS packets.
+
+Modules perform an action when RADIUS packets are received. Modules
+which do more (creating threads, forking programs) will NOT be added
+to the server, and the server core will NOT be modified to enable
+these kinds of modules. Those functions more properly belong in a
+separate application.
+
+Modules ARE permitted to open sockets to other network programs, and
+to send and receive data on those sockets. Modules are NOT permitted
+to open sockets, and to listen for requests. Only the server core has
+that functionality, and it only listens for RADIUS requests.
+
+
+Module outline
+^^^^^^^^^^^^^^
+
+The fundamental concepts of the rlm interface are module, instance,
+and component.
+
+A module is a chunk of code that knows how to deal with a particular
+kind of database, or perhaps a collection of similar
+databases. Examples:
+
+- rlm_sql contains code for talking to MySQL or Postgres, and for mapping RADIUS records onto SQL tables
+- rlm_unix contains code for making radiusd fit in well on unix-like systems, including getpw* authentication and utmp/wtmp-style logging.
+
+An instance specifies the actual location of a collection data that
+can be used by a module. Examples:
+
+- /var/log/radutmp
+- "the MySQL database on bigserver.theisp.com.example"
+
+A module can have multiple components which act on
+RADIUS requests at different stages. The components are:
+
+- authorization: check that a user exists, decide on an authentication method or proxy realm, and possibly apply some attributes to be returned in the reply packet.
+- authentication: verify that the password is correct.
+- preaccounting: decide whether to proxy the request, and possibly add attributes that should be included in any logs
+- accounting: record the request in the log
+- checksimul: count the number of active sessions for the user
+- postauth: process the response before it's sent to the NAS
+- preproxy: process a request before it's proxied
+- postproxy: filter attributes from a reply to a proxied request
+
+A module declares which components it supports by putting function
+pointers in its "module_t rlm_*" structure.
+
+
+Module configuration
+^^^^^^^^^^^^^^^^^^^^
+
+The administrator requests the creation of a module instance by adding
+it inside the modules{} block in radiusd.conf. The instance definition
+looks like this::
+
+ module_name [instance_name] {
+ param1 = value1
+ param2 = value2
+ param3 = value3
+ ...
+ }
+
+The module_name is used to load the module. To see the names of the available
+modules, look for the rlm\_\*.so files in $installprefix/lib. The module_name
+is that, minus the rlm\_ and the .so.
+
+instance_name is an identifier for distinguishing multiple instances of the
+same module. If you are only loading a module once, you can leave out the
+instance_name and it will be assumed to be the same as the module_name.
+
+The parameters inside the module block are passed without interpretation to
+the module and generally point to the exact location of a database or enable
+optional features of the module. Each module should document what parameters
+it accepts and what they do.
+
+For each Access-Request that comes to the server, the authorize{}
+block is called. Then one of the Auth-Type{} blocks from authenticate{}
+is called, depending on the Auth-Type attribute that was chosen by
+authorize{}. Finally, the post-auth{} block is called. If authorize{}
+set the Proxy-To-Realm attribute, then proxying takes over via
+pre-proxy{} and post-proxy{}, and the local authenticate{} phase is
+skipped.
+
+For each Accounting-Request that comes to the server, the preacct{} block is
+called, followed by the accounting{} block. accounting{} is skipped if
+preacct{} sets Proxy-To-Realm.
+
+For an explanation of what "calling" a config block means, see
+the "configurable_failover" file.
+
+
+The lifetime of a module
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+When the server is starting up, or reinitializing itself as a result of a
+SIGHUP, it reads the modules{} section. Each configured module will be loaded
+and its init() method will be called::
+
+ int init(void)
+
+The init() method should take care of
+any setup that is not tied to a specific instance. It will only be called
+once, even if there are multiple instances configured.
+
+For each configured instance, after the init() method, the instantiate()
+method is called. It is given a handle to the configuration block holding its
+parameters, which it can access with cf_section_parse().::
+
+ int instantiate(CONF_SECTION \*cs, void \**instance)
+
+The instantiate() function should look up options in the config section, and
+open whatever files or network connections are necessary for the module to do
+its job. It also should create a structure holding all of the persistent
+variables that are particular to this instance (open file descriptors,
+configured pathnames, etc.) and store a pointer to it in \*instance. That
+void \* becomes a handle (some would call it a "cookie") representing this
+instance. The instance handle is passed as a parameter in all subsequent
+calls to the module's methods, so they can determine which database they are
+supposed to act on.
+
+The authorize(), authenticate(), preaccounting(), and accounting() functions
+are all called the same way::
+
+ int authorize(void \*instance, REQUEST \*request)
+ int authenticate(void \*instance, REQUEST \*request)
+ int preaccounting(void \*instance, REQUEST \*request)
+ int accounting(void \*instance, REQUEST \*request)
+
+they each receive the instance handle and the request, and are expected to
+act on the request using the database pointed to by the instance handle
+(which was set by the instantiate() function).
+
+When the server is being shut down (as the first part of SIGHUP for example)
+detach() is called for each module instance.::
+
+ int detach(void \*instance)
+
+The detach() method should release whatever resources were allocated by the
+instantiate() method.
+
+After all instances are detached, the destroy() method is called.::
+
+ int destroy(void)
+
+It should release resources that were acquired by the init() method.
+
+--Alan Curry <pacman@world.std.com>
diff --git a/doc/developer/release-method.rst b/doc/developer/release-method.rst
new file mode 100644
index 0000000..6e218c0
--- /dev/null
+++ b/doc/developer/release-method.rst
@@ -0,0 +1,42 @@
+Release Method
+==============
+
+As of 2.0, the release process is much simpler. Edit the
+Changelog with the version number and any last updates.
+
+vi doc/ChangeLog
+git commit doc/ChangeLog
+
+
+ Change version numbers in the VERSION file:
+
+vi VERSION
+git commit VERSION
+
+
+ Make the files
+
+ Note that it also does "make dist-check", which checks
+ the build rules for various packages.
+
+make dist
+
+
+ Validate that the packages are OK. If so, tag the release.
+
+ Note that this does NOT actually do the tagging! You will
+ have to run the command it prints out yourself.
+
+make dist-tag
+
+
+ Sign the packages. You will need the correct GPG key for this
+ to work.
+
+make dist-sign
+
+
+ Push to the FTP site. You will need write access to the FTP site
+ for this to work.
+
+make dist-publish