HowToWriteALighttpdPlugin » History » Revision 20
Revision 19 (gstrauss, 2020-07-09 21:08) → Revision 20/23 (gstrauss, 2021-07-14 06:44)
h1. Writing Plugins '''Writing Plugins''' Before you start writing your own plugin, you should be able to build lighttpd ([[DevelGit|lighttpd source code and build instructions]]) and should feel comfortable with the two basic data types datatypes in lighttpd and the main headers in lighttpd: headers: * buffer ("buffer.h":https://git.lighttpd.net/lighttpd/lighttpd1.4/src/branch/master/src/buffer.h) ("buffer.c":source:/trunk/src/buffer.c#latest) * array ("array.h":https://git.lighttpd.net/lighttpd/lighttpd1.4/src/branch/master/src/array.h) ("array.c":source:/trunk/src/array.c#latest) * chunk ("chunk.h":https://git.lighttpd.net/lighttpd/lighttpd1.4/src/branch/master/src/chunk.h) * request ("request.h":https://git.lighttpd.net/lighttpd/lighttpd1.4/src/branch/master/src/request.h) * global structures ("base.h":https://git.lighttpd.net/lighttpd/lighttpd1.4/src/branch/master/src/base.h) ("base.h":source:/trunk/src/base.h#latest) Next read the server internals: * [[Docs_InternalHTTPStates|The State Engine of lighttpd]] * [[Docs_InternalPlugins|Plugin Interface]] Review some simple modules, such as "mod_access.c":https://git.lighttpd.net/lighttpd/lighttpd1.4/src/branch/master/src/mod_access.c or "mod_staticfile.c":https://git.lighttpd.net/lighttpd/lighttpd1.4/src/branch/master/src/mod_staticfile.c for a basic idea of how the modules work. h2. The Skeleton Make sure that * automake 1.8.x or higher * autoconf 2.57 or higher * libtool 1.5.x or higher are installed on your system. A good starting point for writing your own plugin is the shipped "mod_skeleton.c":https://git.lighttpd.net/lighttpd/lighttpd1.4/src/branch/master/src/mod_skeleton.c mod_skeleton.c which just contains a basic skeleton. skeleton and a semi-working copy of the mod_access module. mod_access is really simple and should be taken as a basic guide how the modules work. To turn mod_skeleton into a useful plugin take your favorite editor and replace all occurrences occurances of the word 'skeleton' by the name of your plugin: <pre> $ cd src/ $ cp mod_skeleton.c mod_counter.c $ vi mod_counter.c :%s/skeleton/counter/g </pre> and add some lines to the @src/Makefile.am@ *src*/Makefile.am to tell the build system about your new plugin: <pre> lib_LTLIBRARIES += mod_counter.la mod_counter_la_SOURCES = mod_counter.c mod_counter_la_LDFLAGS = $(common_module_ldflags) mod_counter_la_LIBADD = $(common_libadd) </pre> Call @make@ By default, the build system will assume that you are a user and if all goes well, @src/mod_counter.la@ and @src/mod_counter.lo@ only want to compile the existing code. Changes to the Makefile.am files will be not result in your build directory an update to the Makefiles. You have to tell 'configure' that you are a brave developer: <pre> $ ./configure ... </pre> This will automaticly result in a rebuild of the Makefile.am for you and whenever you update the module Makefile.am like adding new dependency libs the Makefile will be linked in @src/.libs/mod_counter.so@ rebuilt and everything is fine. Aside: while While you are developing you might choose to use an alternative path a prefix into which to install all files, files too as you don't want to interfere with the real installation: <pre> $ ./configure --prefix=${HOME}/testbed/lighttpd-1.4.x/ ... </pre> This also removes the burden to install everything as root. Just change your config to use a port greater than 1024 and root-privileges aren't needed to run lighttpd. If configure is not working properly, you may need to regenerate the build system for your version of autoconf. Type <pre> ([[DevelGit|lighttpd source code autoreconf -fi ./configure ... </pre> If all goes well, mod_counter.la and mod_counter.lo files will be in your build instructions]]) directory. h2. The code layout Now take a look at your plugin, mod_counter. You should see: * the configuration structures: plugin_config and plugin_data * the init code for the structures * set_defaults to parse the configuration * the patch-function which applies the conditionals * the real work code and finally * plugin_init function which is called once the plugin is registered into the server h3. plugin_config Every plugin can opt-in to handle conditionals from the global configfile and store the different settings in a by-condition plugin_config. This struct is linked in the next plugin-wide struct: plugin_data h3. plugin_data Each plugin has some local data which only belongs to the plugin (like the config and some temporary buffers). h3. handler_ctx If a plugin has to store connection specific information, you need another structure with one entry per connection. Take a look at mod_rewrite as an example. h3. neccesary functions * _init * _free * _set_defaults * _plugin_init h4. _plugin_init Each plugin has to have a ..._plugin_init function which is called when the plugin is called. It should only set p->name to a buffer containing the name of the plugin (human readable) and should fill the hooks (init, set_defaults, cleanup) and return with 0; h4. _init The ..._init function is called to initialize the plugin itself and returns the plugin_data struct with some usefull defaults. h4. _free ..._free is called at the end of the life of a plugin and is used to tell the plugin to release all allocated memory. Keep in mind that it is preferred not to let the program termination clean up memory for you. Free what ever you have malloced. Use valgrind or something else to verify your work. h4. _set_defaults As soon as the config file is parsed, each plugin gets the chance to fetch its config-values from the config and verify it internally. A config_plugin_keys_t struct contains the config-key and the type you expect. Don't forget that the last entry of the struct should contain a NULL name to mark the end. If you don't care about conditionals, the set_defaults function is quite simple: * set the destinations of the config_plugin_keys_t * call config_plugin_values_init() h4. conditionals The patch function sets the basic defaults and applies the necessary modifications for the currently valid conditionals. Don't forget to check if the patched config makes sense when you use it. The patch functions have to be called as soon as one of the work-handlers is called (like _uri_handler and friends). <pre> mod_counter_patch_config(r, p); </pre> h3. return values In most cases, you will only work with HANDLER_GO_ON, HANDLER_FINISHED and HANDLER_ERROR. HANDLER_GO_ON is returned in most of the cases when you want another plugin to have a chance to touch the request too. If you know that this request is not for you, just return HANDLER_GO_ON and you are done. HANDLER_ERROR should only be called if something fatal happens as it terminates the current connection if called in one of the handlers or terminates lighttpd if called in _set_defaults. HANDLER_FINISHED is the final return-code used to signal that everything is ready to serve the request like * a non-200 status-code has been set * content has been generated you everything is in place HANDLER_WAIT_FOR_EVENT and HANDLER_WAIT_FOR_FD should be returned if you are not finished yet and have to wait for an fd-event or if you ran out of FDs. HANDLER_COMEBACK is useful if you want to re-check the request-structure. This is used in mod_rewrite to handle the rewritten URI.