blob: fdda1c9d1ba7bb3b3ac44c7af86dee60693e5e38 [file] [log] [blame]
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Rivet Internals</title><link rel="stylesheet" type="text/css" href="rivet.css"><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="index.html" title="Apache Rivet"><link rel="up" href="index.html" title="Apache Rivet"><link rel="prev" href="help.html" title="Resources - How to Get Help"><link rel="next" href="upgrading.html" title="Upgrading from mod_dtcl or NeoWebScript"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Rivet Internals</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="help.html"><img src="images/prev.png" alt="Prev"></a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="upgrading.html"><img src="images/next.png" alt="Next"></a></td></tr></table></div><div class="section"><div class="titlepage"><div><div><hr><h2 class="title" style="clear: both"><a name="internals"></a>Rivet Internals</h2></div></div></div><p style="width:90%">
This section easily falls out of date, as new code is added, old
code is removed, and changes are made. The best place to look
is the source code itself. If you are interested in the changes
themselves, the Subversion revision control system
(<span style="font-family:monospace"><span class="command"><strong>svn</strong></span></span>) can provide you with information about
what has been happening with the code.
</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp60001328"></a>Initialization</h3></div></div></div><p style="width:90%">
When Apache is started, (or when child Apache processes are
started if a threaded Tcl is used),
<code class="function">Rivet_InitTclStuff</code> is called, which
creates a new interpreter, or one interpreter per virtual
host, depending on the configuration. It also initializes
various things, like the <span class="structname">RivetChan</span>
channel system, creates the Rivet-specific Tcl commands, and
executes Rivet's <code class="filename">init.tcl</code>. The caching
system is also set up, and if there is a
<span style="font-family:monospace"><span class="command"><strong>GlobalInitScript</strong></span></span>, it is run.
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp59987808"></a>RivetChan</h3></div></div></div><p style="width:90%">
The <span class="structname">RivetChan</span> system was created in
order to have an actual Tcl channel that we could redirect
standard output to. This lets us use, for instance, the
regular <span style="font-family:monospace"><span class="command"><strong>puts</strong></span></span> command in .rvt pages. It
works by creating a channel that buffers output, and, at
predetermined times, passes it on to Apache's IO system.
Tcl's regular standard output is replaced with an instance of
this channel type, so that, by default, output will go to the
web page.
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp60051696"></a>The <span style="font-family:monospace"><span class="command"><strong>global</strong></span></span> Command</h3></div></div></div><p style="width:90%">
Rivet aims to run standard Tcl code with as few surprises as
possible. At times this involves some compromises - in this
case regarding the <span style="font-family:monospace"><span class="command"><strong>global</strong></span></span> command. The
problem is that the command will create truly global
variables. If the user is just cut'n'pasting some Tcl code
into Rivet, they most likely just want to be able to share the
variable in question with other procs, and don't really care
if the variable is actually persistant between pages. The
solution we have created is to create a proc
<span style="font-family:monospace"><span class="command"><strong>::request::global</strong></span></span> that takes the place of
the <span style="font-family:monospace"><span class="command"><strong>global</strong></span></span> command in Rivet templates. If
you really need a true global variable, use either
<span style="font-family:monospace"><span class="command"><strong>::global</strong></span></span> or add the :: namespace qualifier
to variables you wish to make global.
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp60057168"></a>Page Parsing, Execution and Caching</h3></div></div></div><p style="width:90%">
When a Rivet page is requested, it is transformed into an
ordinary Tcl script by parsing the file for the &lt;? ?&gt;
processing instruction tags. Everything outside these tags
becomes a large <span style="font-family:monospace"><span class="command"><strong>puts</strong></span></span> statement, and
everything inside them remains Tcl code.
</p><p style="width:90%">
Each .rvt file is evaluated in its own
<code class="constant">::request</code> namespace, so that it is not
necessary to create and tear down interpreters after each
page. By running in its own namespace, though, each page will
not run afoul of local variables created by other scripts,
because they will be deleted automatically when the namespace
goes away after Apache finishes handling the request.
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top">
One current problem with this system is that while
variables are garbage collected, file handles are not, so
that it is very important that Rivet script authors make
sure to close all the files they open.
</td></tr></table></div><p style="width:90%">
</p><p style="width:90%">
After a script has been loaded and parsed into it's "pure Tcl"
form, it is also cached, so that it may be used in the future
without having to reload it (and re-parse it) from the disk.
The number of scripts stored in memory is configurable. This
feature can significantly improve performance.
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp60061984"></a>Extending Rivet by developing C procedures implementing new commands</h3></div></div></div><p style="width:90%">
Rivet endows the Tcl interpreter with new commands
serving as interface between the application layer and the
Apache web server. Many of these commands
are meaningful only when a HTTP request is under way and
therefore a request_rec object allocated by the framework
is existing and was passed to mod_rivet as argument of a callback.
In case commands have to gain access to a valid request_rec
object the C procedure must check if such
a pointer exists and it's initialized
with valid data. For this purpose the procedure handling requests
(Rivet_SendContent) makes a copy of such pointer and keeps it
in an internal structure. The copy is set to NULL just before
returning to the framework, right after mod_rivet's has
carried out its request processing. When the pointer copy is NULL
the module is outside any request processing and this
condition invalidates the execution of
many of the Rivet commands. In case they are called
(for example in a ChildInitScript, GlobalInitScript,
ServerInitScript or ChildExitScript) they fail with a Tcl error
you can handle with a <span style="font-family:monospace"><span class="command"><strong>catch</strong></span></span> command.
</p><p style="width:90%">
For this purpose in src/rivet.h the macro
CHECK_REQUEST_REC was defined accepting two arguments: the copy
to the request_rec pointer (stored in the
<span class="structname">rivet_interp_globals</span>
structure) and the command name. If the pointer is NULL
the macro calls Tcl_NoRequestRec and returns TCL_ERROR
causing the command to fail. These are the step to follow
to implement a new C language command for mod_rivet
</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
Define the command and associated C language procedure
in src/rivetcmds/rivetCore.c using the macro
RIVET_OBJ_CMD<pre class="programlisting">RIVET_OBJ_CMD("mycmd",Rivet_MyCmd)</pre>
This macro ensures the command is defined as <span style="font-family:monospace"><span class="command"><strong>::rivet::mycmd</strong></span></span></li><li class="listitem">
Add the code of Rivet_MyCmd to src/rivetcmd/rivetCore.c (in case
the code resides in a different file also src/Makefile.am should be
changed to tell the build system how to compile the code and
link it into mod_rivet.so)
</li><li class="listitem">
If the code must gain access to <span style="font-family:monospace"><span class="command"><strong>globals-&gt;r</strong></span></span>
put add the macro testing for the pointer
<pre class="programlisting">TCL_CMD_HEADER( Rivet_MyCmd )
{
rivet_interp_globals *globals = Tcl_GetAssocData( interp, "rivet", NULL );
....
CHECK_REQUEST_REC(globals-&gt;r,"::rivet::mycmd");
...
}</pre></li><li class="listitem">
Add a test for this command in tests/checkfails.tcl. For
instance
<pre class="programlisting">...
check_fail no_body
check_fail virtual_filename unkn
check_fail my_cmd &lt;arg1&gt; &lt;arg2&gt;
....</pre>
Where &lt;arg1&gt; &lt;arg2&gt; are optional
arguments in case the command needs to check for <span style="font-family:monospace"><span class="command"><strong>globals-&gt;r</strong></span></span>
in special cases. Then, if <span style="font-family:monospace"><span class="command"><strong>::rivet::mycmd</strong></span></span> must fail also
tests/failtest.tcl should modified as
<pre class="programlisting">virtual_filename-&gt;1
mycmd-&gt;1</pre>
The value associated to the test must be 0 in case the
command doesn't need to test the <span style="font-family:monospace"><span class="command"><strong>globals-&gt;r</strong></span></span> pointer.
</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp60078864"></a>Debugging Rivet and Apache</h3></div></div></div><p style="width:90%">
If you are interested in hacking on Rivet, you're welcome to
contribute! Invariably, when working with code, things go
wrong, and it's necessary to do some debugging. In a server
environment like Apache, it can be a bit more difficult to
find the right way to do this. Here are some techniques to
try.
</p><p style="width:90%">
The first thing you should know is that Apache can be launched
as a <span class="emphasis"><em>single process</em></span> with the
-X argument:</p><pre class="programlisting">httpd -X</pre>.
<p style="width:90%">
On Linux, one of the first things to try is the system call
tracer, <span style="font-family:monospace"><span class="command"><strong>strace</strong></span></span>. You don't even have to
recompile Rivet or Apache for this to work.
</p><pre class="programlisting">strace -o /tmp/outputfile -S 1000 httpd -X</pre><p style="width:90%">
This command will run httpd in the system call tracer,
which leaves its output (there is potentially a lot of it) in
<code class="filename">/tmp/outputfile</code>. The -S
option tells <span style="font-family:monospace"><span class="command"><strong></strong></span></span>strace to only record the
first 1000 bytes of a syscall. Some calls such as
<code class="function">write</code> can potentially be much longer than
this, so you may want to increase this number. The results
are a list of all the system calls made by the program. You
want to look at the end, where the failure presumably occured,
to see if you can find anything that looks like an error. If
you're not sure what to make of the results, you can always
ask on the Rivet development mailing list.
</p><p style="width:90%">
If <span style="font-family:monospace"><span class="command"><strong>strace</strong></span></span> (or its equivalent on your
operating system) doesn't answer your question, it may be time
to debug Apache and Rivet. To do this, you will need to rebuild mod_rivet.
First of all you have to configure the build by running the
<span style="font-family:monospace"><span class="command"><strong>./configure</strong></span></span> script with the
-enable-symbols option and after you have
set the CFLAGS and LDFLAGS environment variables
</p><pre class="programlisting">export CFLAGS="-g -O0"
export LDFLAGS="-g"
./configure --enable-symbols ......
make
make install</pre><p style="width:90%">
Arguments to <span style="font-family:monospace"><span class="command"><strong>./configure</strong></span></span> must fit your Apache HTTP
web server installation. See the output produced by
</p><pre class="programlisting">./configure --help</pre><p style="width:90%">
And check the <a class="xref" href="installation.html" title="Apache Rivet Installation">the section called “Apache Rivet Installation”</a> page to
have further information.
Since it's easier to debug a single process, we'll still run
Apache in single process mode with -X:
</p><pre class="programlisting">
@ashland [~] $ gdb /usr/sbin/apache.dbg
GNU gdb 5.3-debian
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "powerpc-linux"...
(gdb) run -X
Starting program: /usr/sbin/apache.dbg -X
[New Thread 16384 (LWP 13598)]
.
.
.
</pre><p style="width:90%">
When your apache session is up and running, you can request a
web page with the browser, and see where things go wrong (if
you are dealing with a crash, for instance).
</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="help.html"><img src="images/prev.png" alt="Prev"></a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="upgrading.html"><img src="images/next.png" alt="Next"></a></td></tr><tr><td width="40%" align="left" valign="top">Resources - How to Get Help </td><td width="20%" align="center"><a accesskey="h" href="index.html"><img src="images/home.png" alt="Home"></a></td><td width="40%" align="right" valign="top"> Upgrading from mod_dtcl or NeoWebScript</td></tr></table></div></body></html>