 .gitignore                             |   16 ++-
 Makefile                               |   71 +++++++--
 README                                 |    5 +-
 bindings/{python2 => python}/fiu.py    |   14 +-
 bindings/{python2 => python}/fiu_ll.c  |   52 +++++-
 bindings/python/setup.py               |   29 +++
 bindings/python2/LICENSE               |   30 ----
 bindings/python2/setup.py              |   17 --
 doc/guide.rst                          |    4 +-
 doc/posix.rst                          |  103 +++++++++++
 doc/remote_control.rst                 |   43 +++++
 libfiu/Makefile                        |    4 +-
 libfiu/fiu-control.h                   |   13 ++-
 libfiu/fiu-rc.c                        |  281 ++++++++++++++++++++++++++++++
 libfiu/fiu.c                           |  120 ++++++++++++-
 libfiu/fiu.h                           |    4 -
 libfiu/internal.h                      |   11 ++
 libfiu/libfiu.3                        |   25 ++-
 libfiu/libfiu.skel.pc                  |    2 +-
 preload/Makefile                       |   34 ++++
 preload/posix/Makefile                 |   73 ++++++++
 preload/posix/codegen.c                |   42 +++++
 preload/posix/codegen.h                |  135 +++++++++++++++
 preload/posix/function_list.skel       |   18 ++
 preload/posix/generate                 |  293 ++++++++++++++++++++++++++++++++
 preload/posix/modules/libc.mm.custom.c |  105 ++++++++++++
 preload/posix/modules/linux.io.mod     |   16 ++
 preload/posix/modules/posix.custom.c   |   67 ++++++++
 preload/posix/modules/posix.io.mod     |  195 +++++++++++++++++++++
 preload/posix/modules/posix.mm.mod     |   68 ++++++++
 preload/posix/modules/posix.proc.mod   |   41 +++++
 preload/posix/utils/extract_from_man   |  164 ++++++++++++++++++
 preload/run/Makefile                   |   57 ++++++
 preload/run/fiu-run.1                  |   80 +++++++++
 preload/run/fiu-run.in                 |  122 +++++++++++++
 preload/run/run.c                      |  129 ++++++++++++++
 utils/Makefile                         |   18 ++
 utils/fiu-ctrl                         |  158 +++++++++++++++++
 utils/fiu-ctrl.1                       |   73 ++++++++
 39 files changed, 2624 insertions(+), 108 deletions(-)

diff --git a/.gitignore b/.gitignore
index e0c7c3d..68ac8d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,17 @@
-bindings/python2/build
-bindings/python2/*.pyc
-bindings/python2/*.pyo
+bindings/python/build
+bindings/python/*.pyc
+bindings/python/*.pyo
 libfiu/*.o
 libfiu/libfiu.a
 libfiu/libfiu.pc
 libfiu/libfiu.so
+preload/posix/*.o
+preload/posix/*.so
+preload/posix/function_list
+preload/posix/modules/*.o
+preload/posix/modules/*.mod.c
+preload/posix/modules/*.mod.fl
+preload/run/*.o
+preload/run/*.so
+preload/run/fiu-run
+
diff --git a/Makefile b/Makefile
index a05295c..4aa8326 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,75 @@
 
-all: default
 
-default: libfiu utils
+default: all
+
+install: all_install
+
+all: libfiu preload utils
+
+all_install: libfiu_install preload_install utils_install
+
 
 libfiu:
 	$(MAKE) -C libfiu
 
-install:
+libfiu_clean:
+	$(MAKE) -C libfiu clean
+
+libfiu_install:
 	$(MAKE) -C libfiu install
 
 
-python2:
-	cd bindings/python2 && python setup.py build
 
-python2_install:
-	cd bindings/python2 && python setup.py install
+preload: libfiu
+	$(MAKE) -C preload
 
-python2_clean:
-	cd bindings/python2 && rm -rf build/
+preload_clean:
+	$(MAKE) -C preload clean
 
+preload_install: preload
+	$(MAKE) -C preload install
 
-clean: python2_clean
-	$(MAKE) -C libfiu clean
+
+utils:
+	$(MAKE) -C utils
+
+utils_clean:
+	$(MAKE) -C utils clean
+
+utils_install: utils
+	$(MAKE) -C utils install
+
+
+bindings: python2 python3
+
+bindings_install: python2_install python3_install
+
+bindings_clean: python_clean
+
+python2: libfiu
+	cd bindings/python && python setup.py build
+
+python2_install: python2
+	cd bindings/python && python setup.py install
+
+python3: libfiu
+	cd bindings/python && python3 setup.py build
+
+python3_install: python3
+	cd bindings/python && python3 setup.py install
+
+python_clean:
+	cd bindings/python && rm -rf build/
+
+
+clean: python_clean preload_clean libfiu_clean utils_clean
 
 
-.PHONY: default all clean libfiu utils \
-	python2 python2_install python2_clean
+.PHONY: default all clean install all_install \
+	libfiu libfiu_clean libfiu_install \
+	python2 python2_install python3 python3_install python_clean \
+	bindings bindings_install bindings_clean \
+	preload preload_clean preload_install \
+	utils utils_clean utils_install
 
 
diff --git a/README b/README
index 8ba4ef7..312e4ed 100644
--- a/README
+++ b/README
@@ -7,7 +7,7 @@ libfiu is a C library for fault injection. It provides functions to mark
 enable/disable the failure of those points (the "control API"). It's in the
 public domain, see the LICENSE file for more information.
 
-The core API is used inside the code wanting to perform failure injection on.
+The core API is used inside the code wanting to perform fault injection on.
 The control API is used inside the testing code, in order to control the
 injection of failures.
 
@@ -31,7 +31,8 @@ for installing. By default it installs into /usr/local, but you can provide an
 alternative prefix by running "make PREFIX=/my/prefix install".
 
 To build the Python bindings, use "make python2"; to install them you can run
-"make python2_install".
+"make python2_install". For the Python 3 bindings, use "make python3" and
+"make python3_install".
 
 
 Where to report bugs
diff --git a/bindings/python2/fiu.py b/bindings/python/fiu.py
similarity index 87%
rename from bindings/python2/fiu.py
rename to bindings/python/fiu.py
index f4e6ece..c61eef9 100644
--- a/bindings/python2/fiu.py
+++ b/bindings/python/fiu.py
@@ -36,7 +36,7 @@ def enable(name, failnum = 1, failinfo = None, flags = 0):
 	r = _ll.enable(name, failnum, failinfo, flags)
 	if r != 0:
 		del _fi_table[name]
-		raise RuntimeError, r
+		raise RuntimeError(r)
 
 def enable_random(name, probability, failnum = 1, failinfo = None, flags = 0):
 	"Enables the given point of failure, with the given probability."
@@ -44,7 +44,7 @@ def enable_random(name, probability, failnum = 1, failinfo = None, flags = 0):
 	r = _ll.enable_random(name, failnum, failinfo, flags, probability)
 	if r != 0:
 		del _fi_table[name]
-		raise RuntimeError, r
+		raise RuntimeError(r)
 
 def enable_external(name, cb, failnum = 1, flags = 0):
 	"""Enables the given point of failure, leaving the decision whether to
@@ -61,7 +61,7 @@ def enable_external(name, cb, failnum = 1, flags = 0):
 	_fi_table[name] = cb
 	r = _ll.enable_external(name, failnum, flags, cb)
 	if r != 0:
-		raise RuntimeError, r
+		raise RuntimeError(r)
 
 def disable(name):
 	"""Disables the given point of failure, undoing the actions of the
@@ -70,6 +70,12 @@ def disable(name):
 		del _fi_table[name]
 	r = _ll.disable(name)
 	if r != 0:
-		raise RuntimeError, r
+		raise RuntimeError(r)
 
+def rc_fifo(basename):
+	"""Enables remote control over a named pipe that begins with the given
+	basename. The final path will be "basename-$PID"."""
+	r = _ll.rc_fifo(basename)
+	if r != 0:
+		raise RuntimeError(r)
 
diff --git a/bindings/python2/fiu_ll.c b/bindings/python/fiu_ll.c
similarity index 80%
rename from bindings/python2/fiu_ll.c
rename to bindings/python/fiu_ll.c
index 8afb439..a558f28 100644
--- a/bindings/python2/fiu_ll.c
+++ b/bindings/python/fiu_ll.c
@@ -1,10 +1,11 @@
 
 /*
- * Python bindings for libfiu
+ * Python 2/3 bindings for libfiu
  * Alberto Bertogli (albertito@blitiri.com.ar)
  *
  * This is the low-level module, used by the python one to construct
- * friendlier objects.
+ * friendlier objects. It support both Python 2 and 3, assuming the constants
+ * PYTHON2 and PYTHON3 are defined accordingly.
  */
 
 #include <Python.h>
@@ -68,9 +69,9 @@ static PyObject *enable_random(PyObject *self, PyObject *args)
 	int failnum;
 	PyObject *failinfo;
 	unsigned int flags;
-	float probability;
+	double probability;
 
-	if (!PyArg_ParseTuple(args, "siOIf:enable_random", &name, &failnum,
+	if (!PyArg_ParseTuple(args, "siOId:enable_random", &name, &failnum,
 				&failinfo, &flags, &probability))
 		return NULL;
 
@@ -119,10 +120,10 @@ static int external_callback(const char *name, int *failnum, void **failinfo,
 		return 0;
 	}
 
-	/* If PyInt_AsLong() causes an error, it will be handled by the
+	/* If PyLong_AsLong() causes an error, it will be handled by the
 	 * PyErr_Occurred() check in fail(), so we don't need to worry about
 	 * it now. */
-	rv = PyInt_AsLong(cbrv);
+	rv = PyLong_AsLong(cbrv);
 	Py_DECREF(cbrv);
 
 	PyGILState_Release(gil_state);
@@ -137,7 +138,7 @@ static PyObject *enable_external(PyObject *self, PyObject *args)
 	unsigned int flags;
 	PyObject *py_external_cb;
 
-	if (!PyArg_ParseTuple(args, "siIO:enable_random", &name, &failnum,
+	if (!PyArg_ParseTuple(args, "siIO:enable_external", &name, &failnum,
 				&flags, &py_external_cb))
 		return NULL;
 
@@ -161,31 +162,62 @@ static PyObject *disable(PyObject *self, PyObject *args)
 {
 	char *name;
 
-	if (!PyArg_ParseTuple(args, "s:fail", &name))
+	if (!PyArg_ParseTuple(args, "s:disable", &name))
 		return NULL;
 
 	return PyLong_FromLong(fiu_disable(name));
 }
 
+static PyObject *rc_fifo(PyObject *self, PyObject *args)
+{
+	char *basename;
 
-static PyMethodDef fiu_functions[] = {
+	if (!PyArg_ParseTuple(args, "s:rc_fifo", &basename))
+		return NULL;
+
+	return PyLong_FromLong(fiu_rc_fifo(basename));
+}
+
+static PyMethodDef fiu_methods[] = {
 	{ "fail", (PyCFunction) fail, METH_VARARGS, NULL },
 	{ "failinfo", (PyCFunction) failinfo, METH_VARARGS, NULL },
 	{ "enable", (PyCFunction) enable, METH_VARARGS, NULL },
 	{ "enable_random", (PyCFunction) enable_random, METH_VARARGS, NULL },
 	{ "enable_external", (PyCFunction) enable_external, METH_VARARGS, NULL },
 	{ "disable", (PyCFunction) disable, METH_VARARGS, NULL },
+	{ "rc_fifo", (PyCFunction) rc_fifo, METH_VARARGS, NULL },
 	{ NULL }
 };
 
+#ifdef PYTHON3
+static PyModuleDef fiu_module = {
+	PyModuleDef_HEAD_INIT,
+	.m_name = "libfiu",
+	.m_size = -1,
+	.m_methods = fiu_methods,
+};
+#endif
+
+#ifdef PYTHON2
 PyMODINIT_FUNC initfiu_ll(void)
+#else
+PyMODINIT_FUNC PyInit_fiu_ll(void)
+#endif
 {
 	PyObject *m;
 
-	m = Py_InitModule("fiu_ll", fiu_functions);
+#ifdef PYTHON2
+	m = Py_InitModule("fiu_ll", fiu_methods);
+#else
+	m = PyModule_Create(&fiu_module);
+#endif
 
 	PyModule_AddIntConstant(m, "FIU_ONETIME", FIU_ONETIME);
 
 	fiu_init(0);
+
+#ifdef PYTHON3
+	return m;
+#endif
 }
 
diff --git a/bindings/python/setup.py b/bindings/python/setup.py
new file mode 100644
index 0000000..fed2a78
--- /dev/null
+++ b/bindings/python/setup.py
@@ -0,0 +1,29 @@
+
+import sys
+from distutils.core import setup, Extension
+
+if sys.version_info[0] == 2:
+	ver_define = ('PYTHON2', '1')
+elif sys.version_info[0] == 3:
+	ver_define = ('PYTHON3', '1')
+
+fiu_ll = Extension("fiu_ll",
+		sources = ['fiu_ll.c'],
+		define_macros = [ver_define],
+		libraries = ['fiu'],
+
+		# these two allow us to build without having libfiu installed,
+		# assuming we're in the libfiu source tree
+		include_dirs = ['../../libfiu/'],
+		library_dirs=['../../libfiu/'])
+
+setup(
+	name = 'fiu',
+	description = "libfiu bindings",
+	author = "Alberto Bertogli",
+	author_email = "albertito@blitiri.com.ar",
+	url = "http://blitiri.com.ar/p/libfiu",
+	py_modules = ['fiu'],
+	ext_modules = [fiu_ll]
+)
+
diff --git a/bindings/python2/LICENSE b/bindings/python2/LICENSE
deleted file mode 100644
index edb80f0..0000000
--- a/bindings/python2/LICENSE
+++ /dev/null
@@ -1,30 +0,0 @@
-
-I don't like licenses, because I don't like having to worry about all this
-legal stuff just for a simple piece of software I don't really mind anyone
-using. But I also believe that it's important that people share and give back;
-so I'm placing this work under the following license.
-
-
-BOLA - Buena Onda License Agreement (v1.0)
-------------------------------------------
-
-This work is provided 'as-is', without any express or implied warranty. In no
-event will the authors be held liable for any damages arising from the use of
-this work.
-
-To all effects and purposes, this work is to be considered Public Domain.
-
-
-However, if you want to be "buena onda", you should:
-
-1. Not take credit for it, and give proper recognition to the authors.
-2. Share your modifications, so everybody benefits from them.
-4. Do something nice for the authors.
-5. Help someone who needs it: sign up for some volunteer work or help your
-   neighbour paint the house.
-6. Don't waste. Anything, but specially energy that comes from natural
-   non-renewable resources. Extra points if you discover or invent something
-   to replace them.
-7. Be tolerant. Everything that's good in nature comes from cooperation.
-
-
diff --git a/bindings/python2/setup.py b/bindings/python2/setup.py
deleted file mode 100644
index dbdc256..0000000
--- a/bindings/python2/setup.py
+++ /dev/null
@@ -1,17 +0,0 @@
-
-from distutils.core import setup, Extension
-
-fiu_ll = Extension("fiu_ll",
-		libraries = ['fiu'],
-		sources = ['fiu_ll.c'])
-
-setup(
-	name = 'fiu',
-	description = "libfiu bindings",
-	author = "Alberto Bertogli",
-	author_email = "albertito@blitiri.com.ar",
-	url = "http://blitiri.com.ar/p/libfiu",
-	py_modules = ['fiu'],
-	ext_modules = [fiu_ll]
-)
-
diff --git a/doc/guide.rst b/doc/guide.rst
index eb2b736..cf2ff3f 100644
--- a/doc/guide.rst
+++ b/doc/guide.rst
@@ -80,7 +80,7 @@ you can test how your code behaves under that condition, which was otherwise
 hard to trigger.
 
 As you can see, libfiu's API has to "sides": a core API and a control API. The
-core API is used inside the code wanting to perform failure injection on. The
+core API is used inside the code wanting to perform fault injection on. The
 control API is used inside the testing code, in order to control the injection
 of failures.
 
@@ -104,7 +104,7 @@ it's a testing library.
 
 To that end, you should copy *fiu-local.h* into your source tree, and then
 create an option to do a *fault injection build* that #defines the constant
-*ENABLE_FIU* (usually done by adding ``-DFIU_ENABLE=1`` to your compiler
+*FIU_ENABLE* (usually done by adding ``-DFIU_ENABLE=1`` to your compiler
 flags) and links against libfiu (usually done by adding ``-lfiu`` to your
 linker flags).
 
diff --git a/doc/posix.rst b/doc/posix.rst
new file mode 100644
index 0000000..eecadbf
--- /dev/null
+++ b/doc/posix.rst
@@ -0,0 +1,103 @@
+
+Simulating failures in the POSIX API
+====================================
+
+When developing robust sofware, developers often consider the cases when the
+classic POSIX functions return failure.
+
+Testing that fault-handling code is a problem because under normal conditions
+it's hard to make the POSIX functions fail, and generating abnormal conditions
+is usually difficult.
+
+For example, getting *malloc()* to fail can represent using up all your
+memory, and at that point your test case might not even work. Or getting I/O
+operations to fail might involve filling up the disk which is very
+undesirable, or generating a very special environment which is difficult to
+reproduce.
+
+libfiu comes with some tools that can be used to perform fault injection in
+the POSIX API (which includes the C standard library functions) *without*
+having to modify the application's source code, that can help to simulate
+scenarios like the ones described above in an easy and reproducible way.
+
+
+fiu-run
+-------
+
+The first of those tools is an application called *fiu-run*.
+
+Suppose you want to run the classic program "fortune" (which some would
+definitely consider mission critical) and see how it behaves on the presence
+of *read()* errors. With *fiu-run*, you can do it like this::
+
+  $ fiu-run -x -e posix/io/rw/read -p 1 fortune
+
+That enables the failure point with the name *posix/io/rw/read* with 1%
+probability to fail *on each call*, and then runs fortune. The *-x*
+parameter tells *fiu-run* to enable fault injection in the POSIX API.
+
+Run it several times and you can see that sometimes it works, but sometimes it
+doesn't, reporting an error reading, which means a *read()* failed as
+expected.
+
+When fortune is run, every time fortune calls *read()* it has a 1% chance to
+fail, which selects an errno at random from the list of the ones that read()
+is allowed to return. If you want to select an specific errno, you can do it
+by passing its numerical value using the *-i* parameter.
+
+The name of the failure points are fixed, and there is at least one for each
+function that libfiu supports injecting failures to. Not all POSIX is
+included, but most of the important pieces are, and it is easily extended. See
+below for details.
+
+To see the list of supported functions and names, see the (automatically
+generated) *preload/posix/function_list* file that comes in the libfiu
+tarball.
+
+
+fiu-ctrl
+--------
+
+Sometimes it is more interesting to simulate failures at a given point in time
+instead of from the beginning, as *fiu-run* does.
+
+To that end, you can combine *fiu-run* with the second tool, called
+*fiu-ctrl*.
+
+Let's suppose you want to see what the "top" program does when it can't open
+files. First, we run it with *fiu-run*::
+
+  $ fiu-run -x top
+
+Everything should look normal. Then, in another terminal, we make *open()*
+fail unconditionally::
+
+  $ fiu-ctrl -e posix/io/oc/open `pidof top`
+
+After that moment, the top display will probably be empty, because it can't
+read process information. Now let's disable that failure point, so *open()*
+works again::
+
+  $ fiu-ctrl -d posix/io/oc/open `pidof top`
+
+And everything should have gone back to normal.
+
+
+How does it work
+----------------
+
+libfiu comes with two preload libraries: *fiu_run_preload* and
+*fiu_posix_preload*.
+
+The first one is loaded using *LD_PRELOAD* (see *ld.so(8)* for more
+information) by *fiu-run*, and can enable failure points and start libfiu's
+remote control capabilities before the program begins to run.
+
+The second one is also loaded using *LD_PRELOAD* by *fiu-run* when the
+*-x* parameter is given, and provides libfiu-enabled wrappers for the POSIX
+functions, allowing the user to inject failures in them.
+
+*fiu-ctrl* communicates with the applications launched by
+*fiu-run* via the libfiu remote control capabilities.
+
+
diff --git a/doc/remote_control.rst b/doc/remote_control.rst
new file mode 100644
index 0000000..4db4103
--- /dev/null
+++ b/doc/remote_control.rst
@@ -0,0 +1,43 @@
+
+Remote control
+==============
+
+The library has remote controlling capabilities, so external, unrelated
+processes can enable and disable failure points.
+
+It has a very simple request/reply protocol that can be performed over
+different transports. At the moment, the only transport available is named
+pipes.
+
+Remote control must be enabled by the controlled process using *fiu_rc_fifo()*
+(for named pipes). A set of utilities are provided to enable remote control
+without having to alter the application's source code, which can be useful for
+performing fault injection in libraries, see *fiu-run* and *fiu-ctrl* for more
+information.
+
+
+Remote control protocol
+-----------------------
+
+It is a line based request/reply protocol. Lines end with a newline character
+(no carriage return). A request is composed of a command and 0 or more
+parameters, separated with a single space. The following commands are
+supported at the moment:
+
+ - ``enable <name> <failnum> <failinfo> [flags]``
+ - ``enable_random <name> <failnum> <failinfo> <probability> [flags]``
+ - ``disable <name>``
+
+Where:
+
+ - *name* is the name of the point of failure (which, at the moment, cannot
+   have spaces inside).
+ - *failnum* the same as the *failnum* parameter of *fiu_enable()* (see the
+   manpage for more details).
+ - failinfo the same as the *failinfo* parameter of *fiu_enable()* (see the
+   manpage for more details).
+ - *flags* can be either absent or ``one``, which has the same meaning as
+   passing ``FIU_ONETIME`` in the *flags* parameter to *fiu_enable()*.
+
+The reply is always a number: 0 on success, < 0 on errors.
+
diff --git a/libfiu/Makefile b/libfiu/Makefile
index 17c95d3..425056c 100644
--- a/libfiu/Makefile
+++ b/libfiu/Makefile
@@ -1,6 +1,6 @@
 
 CFLAGS += -std=c99 -pedantic -Wall -O3
-ALL_CFLAGS = -D_XOPEN_SOURCE=500 -fPIC -DENABLE_FIU=1 $(CFLAGS)
+ALL_CFLAGS = -D_XOPEN_SOURCE=500 -fPIC -DFIU_ENABLE=1 $(CFLAGS)
 
 ifdef DEBUG
 ALL_CFLAGS += -g
@@ -15,7 +15,7 @@ endif
 PREFIX=/usr/local
 
 
-OBJS = fiu.o
+OBJS = fiu.o fiu-rc.o
 
 
 ifneq ($(V), 1)
diff --git a/libfiu/fiu-control.h b/libfiu/fiu-control.h
index 4a3baa5..c1859ee 100644
--- a/libfiu/fiu-control.h
+++ b/libfiu/fiu-control.h
@@ -3,7 +3,7 @@
  * Control API for libfiu
  */
 
-/* Flags */
+/* Flags for fiu_enable*() */
 #define FIU_ONETIME 1		/* Only fail once */
 
 
@@ -14,7 +14,7 @@
  * - failinfo: what will fiu_failinfo() return.
  * - flags: flags.
  * - returns: 0 if success, < 0 otherwise.
- * */
+ */
 int fiu_enable(const char *name, int failnum, void *failinfo,
 		unsigned int flags);
 
@@ -57,3 +57,12 @@ int fiu_enable_external(const char *name, int failnum, void *failinfo,
  * - returns: 0 if success, < 0 otherwise. */
 int fiu_disable(const char *name);
 
+/* Enables remote control over a named pipe that begins with the given
+ * basename. "-$PID" will be appended to it to form the final path. After the
+ * process dies, the pipe will be removed. If the process forks, a new pipe
+ * will be created.
+ *
+ * - basename: base path to use in the creation of the named pipes.
+ * - returns: 0 on success, -1 on errors. */
+int fiu_rc_fifo(const char *basename);
+
diff --git a/libfiu/fiu-rc.c b/libfiu/fiu-rc.c
new file mode 100644
index 0000000..83dcb83
--- /dev/null
+++ b/libfiu/fiu-rc.c
@@ -0,0 +1,281 @@
+
+/*
+ * libfiu remote control API
+ */
+
+#include <stdio.h>		/* snprintf() */
+#include <string.h>		/* strncpy() */
+#include <stdlib.h>		/* malloc()/free() */
+#include <sys/param.h>		/* PATH_MAX */
+#include <sys/types.h>		/* getpid(), mknod() */
+#include <unistd.h>		/* getpid(), mknod() */
+#include <sys/stat.h>		/* mknod() */
+#include <fcntl.h>		/* mknod() */
+#include <pthread.h>		/* pthread_create() and friends */
+#include <errno.h>		/* errno and friends */
+
+/* Enable us, so we get the real prototypes from the headers */
+#define FIU_ENABLE 1
+
+#include "fiu-control.h"
+#include "internal.h"
+
+
+/* Max length of a line containing a control directive */
+#define MAX_LINE 512
+
+/*
+ * Generic remote control
+ */
+
+/* Reads a line from the given fd, assumes the buffer can hold MAX_LINE
+ * characters. Returns the number of bytes read, or -1 on error. Inefficient,
+ * but we don't really care. The final '\n' will not be included. */
+static int read_line(int fd, char *buf)
+{
+	int r;
+	char c;
+	unsigned int len;
+
+	c = '\0';
+	len = 0;
+	memset(buf, 0, MAX_LINE);
+
+	do {
+		r = read(fd, &c, 1);
+		if (r < 0)
+			return -1;
+		if (r == 0)
+			break;
+
+		len += r;
+
+		*buf = c;
+		buf++;
+
+	} while (c != '\n' && c != '\0' && len < MAX_LINE);
+
+	if (len > 0 && c == '\n') {
+		*(buf - 1) = '\0';
+		len--;
+	}
+
+	return len;
+}
+
+/* Remote control command processing.
+ * Supported commands:
+ *  - disable <name>
+ *  - enable <name> <failnum> <failinfo> [flags]
+ *  - enable_random <name> <failnum> <failinfo> <probability> [flags]
+ *
+ * flags can be one of:
+ *  - "one": same as FIU_ONETIME
+ */
+static int rc_process_cmd(char *cmd)
+{
+	char *tok, *state;
+	char fp_name[MAX_LINE];
+	int failnum;
+	void *failinfo;
+	float probability;
+	unsigned int flags;
+
+	state = NULL;
+
+	tok = strtok_r(cmd, " ", &state);
+	if (tok == NULL)
+		return -1;
+
+	if (strcmp(tok, "disable") == 0) {
+		tok = strtok_r(NULL, " ", &state);
+		if (tok == NULL)
+			return -1;
+		return fiu_disable(tok);
+
+	} else if (strcmp(tok, "enable") == 0
+			|| strcmp(tok, "enable_random") == 0) {
+
+		tok = strtok_r(NULL, " ", &state);
+		if (tok == NULL)
+			return -1;
+		strncpy(fp_name, tok, MAX_LINE);
+
+		tok = strtok_r(NULL, " ", &state);
+		if (tok == NULL)
+			return -1;
+		failnum = atoi(tok);
+
+		tok = strtok_r(NULL, " ", &state);
+		if (tok == NULL)
+			return -1;
+		failinfo = (void *) strtoul(tok, NULL, 10);
+
+		probability = -1;
+		if (strcmp(tok, "enable_random") == 0) {
+			tok = strtok_r(NULL, " ", &state);
+			if (tok == NULL)
+				return -1;
+			probability = strtof(tok, NULL);
+			if (probability < 0 || probability > 1)
+				return -1;
+		}
+
+		flags = 0;
+		tok = strtok_r(NULL, " ", &state);
+		if (tok != NULL) {
+			if (strcmp(tok, "one") == 0)
+				flags |= FIU_ONETIME;
+		}
+
+		if (probability >= 0) {
+			return fiu_enable_random(fp_name, failnum, failinfo,
+					probability, flags);
+		} else {
+			return fiu_enable(fp_name, failnum, failinfo, flags);
+		}
+	} else {
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Read remote control directives from fdr and process them, writing the
+ * results in fdw. Returns the length of the line read, 0 if EOF, or < 0 on
+ * error. */
+static int rc_do_command(int fdr, int fdw)
+{
+	int len, r, reply_len;
+	char buf[MAX_LINE], reply[MAX_LINE];
+
+	len = read_line(fdr, buf);
+	if (len <= 0)
+		return len;
+
+	r = rc_process_cmd(buf);
+
+	reply_len = snprintf(reply, MAX_LINE, "%d\n", r);
+	r = write(fdw, reply, reply_len);
+	if (r <= 0)
+		return r;
+
+	return len;
+}
+
+
+/*
+ * Remote control via named pipes
+ *
+ * Enables remote control over a named pipe that begins with the given
+ * basename. "-$PID.in" will be appended to it to form the final path to read
+ * commands from, and "-$PID.out" will be appended to it to form the final
+ * path to write the replies to. After the process dies, the pipe will be
+ * removed. If the process forks, a new pipe will be created.
+ */
+
+static char npipe_basename[PATH_MAX];
+static char npipe_path_in[PATH_MAX];
+static char npipe_path_out[PATH_MAX];
+
+static void *rc_fifo_thread(void *unused)
+{
+	int fdr, fdw, r;
+
+	/* increment the recursion count so we're not affected by libfiu,
+	 * otherwise we could make the remote control useless by enabling all
+	 * failure points */
+	rec_count++;
+
+reopen:
+	fdr = open(npipe_path_in, O_RDONLY);
+	if (fdr < 0)
+		return NULL;
+
+	fdw = open(npipe_path_out, O_WRONLY);
+	if (fdw < 0) {
+		close(fdr);
+		return NULL;
+	}
+
+	for (;;) {
+		r = rc_do_command(fdr, fdw);
+		if (r < 0 && errno != EPIPE) {
+			perror("libfiu: Error reading from remote control");
+			close(fdr);
+			close(fdw);
+			goto reopen;
+		} else if (r == 0 || (r < 0 && errno == EPIPE)) {
+			/* one of the ends of the pipe was closed */
+			close(fdr);
+			close(fdw);
+			goto reopen;
+		}
+	}
+
+	close(fdr);
+	close(fdw);
+
+	return NULL;
+}
+
+static void fifo_atexit(void)
+{
+	unlink(npipe_path_in);
+	unlink(npipe_path_out);
+}
+
+static int _fiu_rc_fifo(const char *basename)
+{
+	pthread_t thread;
+
+	/* see rc_fifo_thread() */
+	rec_count++;
+
+	snprintf(npipe_path_in, PATH_MAX,"%s-%d.in", basename, getpid());
+	snprintf(npipe_path_out, PATH_MAX,"%s-%d.out", basename, getpid());
+
+	if (mknod(npipe_path_in, S_IFIFO | 0600, 0) != 0) {
+		rec_count--;
+		return -1;
+	}
+
+	if (mknod(npipe_path_out, S_IFIFO | 0600, 0) != 0) {
+		unlink(npipe_path_in);
+		rec_count--;
+		return -1;
+	}
+
+	if (pthread_create(&thread, NULL, rc_fifo_thread, NULL) != 0) {
+		unlink(npipe_path_in);
+		unlink(npipe_path_out);
+		rec_count--;
+		return -1;
+	}
+
+	atexit(fifo_atexit);
+
+	rec_count--;
+	return 0;
+}
+
+static void fifo_atfork_child(void)
+{
+	_fiu_rc_fifo(npipe_basename);
+}
+
+int fiu_rc_fifo(const char *basename)
+{
+	int r;
+
+	r = _fiu_rc_fifo(basename);
+	if (r < 0)
+		return r;
+
+	strncpy(npipe_basename, basename, PATH_MAX);
+	pthread_atfork(NULL, NULL, fifo_atfork_child);
+
+	return r;
+}
+
+
diff --git a/libfiu/fiu.c b/libfiu/fiu.c
index b1f2330..f0c344b 100644
--- a/libfiu/fiu.c
+++ b/libfiu/fiu.c
@@ -2,12 +2,16 @@
 #include <stdlib.h>		/* malloc() and friends */
 #include <string.h>		/* strcmp() and friends */
 #include <pthread.h>		/* mutexes */
+#include <sys/time.h>		/* gettimeofday() */
+#include <time.h>		/* gettimeofday() */
+#include <limits.h>		/* ULONG_MAX */
 
 /* Enable us, so we get the real prototypes from the headers */
 #define FIU_ENABLE 1
 
 #include "fiu.h"
 #include "fiu-control.h"
+#include "internal.h"
 
 
 /* Different methods to decide when a point of failure fails */
@@ -49,12 +53,29 @@ static struct pf_info *enabled_fails = NULL;
 static struct pf_info *enabled_fails_last = NULL;
 static size_t enabled_fails_len = 0;
 static size_t enabled_fails_nfree = 0;
-static pthread_rwlock_t enabled_fails_lock;
+static pthread_rwlock_t enabled_fails_lock = PTHREAD_RWLOCK_INITIALIZER;
+
 #define ef_rlock() do { pthread_rwlock_rdlock(&enabled_fails_lock); } while (0)
 #define ef_wlock() do { pthread_rwlock_wrlock(&enabled_fails_lock); } while (0)
 #define ef_runlock() do { pthread_rwlock_unlock(&enabled_fails_lock); } while (0)
 #define ef_wunlock() do { pthread_rwlock_unlock(&enabled_fails_lock); } while (0)
 
+/* To prevent unwanted recursive calls that would deadlock, we use a
+ * thread-local recursion count. Unwanted recursive calls can result from
+ * using functions that have been modified to call fiu_fail(), which can
+ * happen when using the POSIX preloader library: fiu_enable() takes the lock
+ * for writing, and can call malloc() (for example), which can in turn call
+ * fiu_fail() which can take the lock for reading.
+ *
+ * It is also modified at fiu-rc.c, to prevent failing within the remote
+ * control thread.
+ *
+ * Sadly, we have to use the GNU extension for TLS, so we do not resort to
+ * pthread_[get|set]specific() which could be wrapped. Luckily it's available
+ * almost everywhere. */
+__thread int rec_count = 0;
+
+
 /* Maximum number of free elements in enabled_fails (used to decide when to
  * shrink). */
 #define EF_MAX_FREE 3
@@ -149,6 +170,46 @@ static int shrink_enabled_fails(void)
 	return 0;
 }
 
+/* Pseudorandom number generator.
+ *
+ * The performance of the PRNG is very sensitive to us, so we implement our
+ * own instead of just use drand48() or similar.
+ *
+ * As we don't really need a very good, thread-safe or secure random source,
+ * we use an algorithm similar to the one used in rand() and drand48() (a
+ * linear congruential generator, see
+ * http://en.wikipedia.org/wiki/Linear_congruential_generator for more
+ * information). Coefficients are the ones used in rand(), so we assume
+ * sizeof(int) >= 4.
+ *
+ * To seed it, we use the current microseconds. To prevent seed reuse, we
+ * re-seed after each fork (see atfork_child()). */
+static unsigned int randd_xn = 0xA673F42D;
+
+static void prng_seed(void)
+{
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL);
+
+	randd_xn = tv.tv_usec;
+}
+
+static double randd(void)
+{
+	randd_xn = 1103515245 * randd_xn + 12345;
+
+	return (double) randd_xn / UINT_MAX;
+}
+
+/* Function that runs after the process has been forked, at the child. It's
+ * registered via pthread_atfork() in fiu_init(). */
+static void atfork_child(void)
+{
+	prng_seed();
+}
+
+
 /*
  * Core API
  */
@@ -158,20 +219,33 @@ static int shrink_enabled_fails(void)
  * time without clashes. */
 int fiu_init(unsigned int flags)
 {
+	rec_count++;
 	ef_wlock();
-	if (!initialized) {
-		/* first time we get called */
-		pthread_key_create(&last_failinfo_key, NULL);
+	if (initialized) {
+		ef_wunlock();
+		rec_count--;
+		return 0;
+	}
 
-		enabled_fails = NULL;
-		enabled_fails_last = NULL;
-		enabled_fails_len = 0;
-		enabled_fails_nfree = 0;
+	pthread_key_create(&last_failinfo_key, NULL);
 
-		initialized = 1;
+	enabled_fails = NULL;
+	enabled_fails_last = NULL;
+	enabled_fails_len = 0;
+	enabled_fails_nfree = 0;
+
+	if (pthread_atfork(NULL, NULL, atfork_child) != 0) {
+		ef_wunlock();
+		rec_count--;
+		return -1;
 	}
 
+	prng_seed();
+
+	initialized = 1;
+
 	ef_wunlock();
+	rec_count--;
 	return 0;
 }
 
@@ -183,10 +257,20 @@ int fiu_fail(const char *name)
 	struct pf_info *pf;
 	int failnum;
 
+	rec_count++;
+
+	/* we must do this before acquiring the lock and calling any
+	 * (potentially wrapped) functions */
+	if (rec_count > 1) {
+		rec_count--;
+		return 0;
+	}
+
 	ef_rlock();
 
 	if (enabled_fails == NULL) {
 		ef_runlock();
+		rec_count--;
 		return 0;
 	}
 
@@ -197,7 +281,7 @@ int fiu_fail(const char *name)
 				goto exit_fail;
 				break;
 			case PF_PROB:
-				if (pf->minfo.probability < drand48() )
+				if (pf->minfo.probability > randd())
 					goto exit_fail;
 				break;
 			case PF_EXTERNAL:
@@ -216,6 +300,7 @@ int fiu_fail(const char *name)
 	}
 
 	ef_runlock();
+	rec_count--;
 	return 0;
 
 exit_fail:
@@ -229,6 +314,7 @@ exit_fail:
 	}
 
 	ef_runlock();
+	rec_count--;
 	return failnum;
 }
 
@@ -284,6 +370,8 @@ static int insert_new_fail(const char *name, int failnum, void *failinfo,
 
 	rv = -1;
 
+	rec_count++;
+
 	/* See if it's already there and update the data if so, or if we have
 	 * a free spot where to put it */
 	ef_wlock();
@@ -334,6 +422,7 @@ static int insert_new_fail(const char *name, int failnum, void *failinfo,
 
 exit:
 	ef_wunlock();
+	rec_count--;
 	return rv;
 }
 
@@ -366,9 +455,18 @@ int fiu_disable(const char *name)
 {
 	struct pf_info *pf;
 
+	rec_count++;
+
 	/* just find the point of failure and mark it as free by setting its
 	 * name to NULL */
 	ef_wlock();
+
+	if (enabled_fails == NULL) {
+		ef_wunlock();
+		rec_count--;
+		return -1;
+	}
+
 	for (pf = enabled_fails; pf <= enabled_fails_last; pf++) {
 		if (name_matches(pf, name, 1)) {
 			disable_pf(pf);
@@ -376,11 +474,13 @@ int fiu_disable(const char *name)
 			if (enabled_fails_nfree > EF_MAX_FREE)
 				shrink_enabled_fails();
 			ef_wunlock();
+			rec_count--;
 			return 0;
 		}
 	}
 
 	ef_wunlock();
+	rec_count--;
 	return -1;
 }
 
diff --git a/libfiu/fiu.h b/libfiu/fiu.h
index e9fdf64..645c456 100644
--- a/libfiu/fiu.h
+++ b/libfiu/fiu.h
@@ -50,10 +50,6 @@ void *fiu_failinfo(void);
 #define fiu_return_on(name, retval) fiu_do_on(name, return retval)
 
 
-/* Undefine the private defines */
-#undef _likely
-#undef _unlikely
-
 #else
 /* fiu not enabled, this should match fiu-local.h but we don't include it
  * because it includes us assuming we're installed system-wide */
diff --git a/libfiu/internal.h b/libfiu/internal.h
new file mode 100644
index 0000000..2c3bfba
--- /dev/null
+++ b/libfiu/internal.h
@@ -0,0 +1,11 @@
+
+/* Some libfiu's internal declarations */
+
+#ifndef _INTERNAL_H
+#define _INTERNAL_H
+
+/* Recursion count, used both in fiu.c and fiu-rc.c */
+extern __thread int rec_count;
+
+#endif
+
diff --git a/libfiu/libfiu.3 b/libfiu/libfiu.3
index 1f27c9b..4178027 100644
--- a/libfiu/libfiu.3
+++ b/libfiu/libfiu.3
@@ -26,6 +26,7 @@ libfiu - Fault injection in userspace
 .BI "		void *" failinfo ", unsigned int " flags ","
 .BI "		external_cb_t *" external_cb ");"
 .BI "int fiu_disable(const char *" name ");"
+.BI "int fiu_rc_fifo(const char *" basename ");"
 .sp
 .fi
 .SH DESCRIPTION
@@ -34,7 +35,7 @@ libfiu is a library for fault injection. It provides functions to mark "points
 of failure" inside your code (the "core API"), and functions to enable/disable
 the failure of those points (the "control API").
 
-The core API is used inside the code wanting to perform failure injection on.
+The core API is used inside the code wanting to perform fault injection on.
 The control API is used inside the testing code, in order to control the
 injection of failures.
 
@@ -52,8 +53,7 @@ runtime, some special considerations were taken to minimize the impact of the
 core API. First of all, if
 .I FIU_ENABLE
 is not defined, then fiu.h will define empty stubs for all the core API,
-effectively disabling fault injection completely, without any runtime
-performance.
+effectively disabling fault injection completely.
 
 Also, a special header
 .I fiu-local.h
@@ -144,8 +144,9 @@ failure that begin with the given name (excluding the asterisk, of course).
 
 .TP
 .BI "fiu_enable_random(" name ", " failnum ", " failinfo ", " flags ", " probability ")"
-Enables the given point of failure, with the given probability. The rest of the
-parameters, as well as the return value, are the same as the ones in
+Enables the given point of failure, with the given probability (between 0 and
+1). The rest of the parameters, as well as the return value, are the same as
+the ones in
 .BR fiu_enable() .
 
 .TP
@@ -162,9 +163,17 @@ Disables the given point of failure, undoing the actions of the
 .B fiu_enable*()
 functions.
 
-.SH BUGS
+.TP
+.BI "fiu_rc_fifo(" basename ")"
+Enables remote control over named pipes with the given basename. See the
+remote control documentation that comes with the library for more detail.
 
+.SH SEE ALSO
+.BR fiu-run (1),
+.BR fiu-ctrl (1).
+
+.SH BUGS
 If you want to report bugs, or have any questions or comments, just let me
-know at albertito@blitiri.com.ar. For more information about the library, you
-can go to http://blitiri.com.ar/p/libfiu.
+know at albertito@blitiri.com.ar. For more information about libfiu, you can
+go to http://blitiri.com.ar/p/libfiu.
 
diff --git a/libfiu/libfiu.skel.pc b/libfiu/libfiu.skel.pc
index 838b4b6..ced4173 100644
--- a/libfiu/libfiu.skel.pc
+++ b/libfiu/libfiu.skel.pc
@@ -6,7 +6,7 @@ includedir=${prefix}/include
 Name: libfiu
 Description: Fault injection in userspace
 URL: http://blitiri.com.ar/p/libfiu/
-Version: 0.10
+Version: 0.11
 Libs: -L${libdir} -lfiu
 Cflags: -I${includedir}
 
diff --git a/preload/Makefile b/preload/Makefile
new file mode 100644
index 0000000..a575d71
--- /dev/null
+++ b/preload/Makefile
@@ -0,0 +1,34 @@
+
+default: all
+
+all: posix run
+
+install: posix_install run_install
+
+clean: posix_clean run_clean
+
+
+posix:
+	$(MAKE) -C posix/
+
+posix_clean:
+	$(MAKE) -C posix/ clean
+
+posix_install:
+	$(MAKE) -C posix/ install
+
+run:
+	$(MAKE) -C run/
+
+run_clean:
+	$(MAKE) -C run/ clean
+
+run_install:
+	$(MAKE) -C run/ install
+
+
+
+.PHONY: default clean install \
+	posix posix_clean posix_install \
+	run run_clean run_install 
+
diff --git a/preload/posix/Makefile b/preload/posix/Makefile
new file mode 100644
index 0000000..e470e70
--- /dev/null
+++ b/preload/posix/Makefile
@@ -0,0 +1,73 @@
+
+CFLAGS += -std=c99 -Wall -O3
+ALL_CFLAGS = -D_XOPEN_SOURCE=500 -fPIC -DFIU_ENABLE=1 \
+		-I. -I../../libfiu/ -L../../libfiu/ $(CFLAGS)
+
+ifdef DEBUG
+ALL_CFLAGS += -g
+endif
+
+ifdef PROFILE
+ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage
+endif
+
+# prefix for installing the binaries
+PREFIX=/usr/local
+
+MODS = $(wildcard modules/*.mod)
+GEN_C = $(addsuffix .c,$(MODS))
+GEN_OBJS = $(addsuffix .o,$(MODS))
+GEN_FL = $(addsuffix .fl,$(MODS))
+CUSTOM_OBJS = $(patsubst %.c,%.o,$(wildcard modules/*.custom.c))
+OBJS = codegen.o $(GEN_OBJS) $(CUSTOM_OBJS)
+
+
+ifneq ($(V), 1)
+	NICE_CC = @echo "  CC  $@"; $(CC)
+	NICE_GEN = @echo "  GEN $@"; ./generate
+	Q = @
+else
+	NICE_CC = $(CC)
+	NICE_GEN = ./generate
+	Q =
+endif
+
+
+default: all
+	
+all: fiu_posix_preload.so function_list
+
+$(GEN_OBJS): $(GEN_C)
+
+%.mod.c: %.mod
+	$(NICE_GEN) $< $@ $<.fl
+
+.c.o:
+	$(NICE_CC) $(ALL_CFLAGS) -c $< -o $@
+
+fiu_posix_preload.so: $(OBJS)
+	$(NICE_CC) $(ALL_CFLAGS) -shared -fPIC $(OBJS) -lfiu -ldl \
+		-o fiu_posix_preload.so
+
+# this should only be needed when building the function list and not the
+# preload library
+%.mod.fl: %.mod
+	$(NICE_GEN) $< $<.c $@
+
+function_list: $(GEN_FL) function_list.skel
+	@echo "  function_list"
+	$(Q) cp function_list.skel function_list
+	$(Q) for i in $(GEN_FL); do cat $$i >> function_list; done
+
+install: fiu_posix_preload.so
+	install -d $(PREFIX)/lib
+	install -m 0755 fiu_posix_preload.so $(PREFIX)/lib
+
+clean:
+	rm -f $(OBJS) $(GEN_OBJS:.o=.c) $(GEN_FL)
+	rm -f function_list fiu_posix_preload.so
+	rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out
+
+.PHONY: default install clean
+
+
diff --git a/preload/posix/codegen.c b/preload/posix/codegen.c
new file mode 100644
index 0000000..e783861
--- /dev/null
+++ b/preload/posix/codegen.c
@@ -0,0 +1,42 @@
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include "codegen.h"
+
+/* Dynamically load libc */
+void *_fiu_libc;
+
+/* Recursion counter, per-thread */
+int __thread _fiu_called;
+
+static void __attribute__((constructor(200))) _fiu_init(void)
+{
+	_fiu_called = 0;
+
+	_fiu_libc = dlopen("libc.so.6", RTLD_NOW);
+	if (_fiu_libc == NULL) {
+		fprintf(stderr, "Error loading libc: %s\n", dlerror());
+		exit(1);
+	}
+
+	printd("done\n");
+}
+
+/* this runs after all function-specific constructors */
+static void __attribute__((constructor(250))) _fiu_init_final(void)
+{
+	struct timeval tv;
+
+	rec_inc();
+
+	fiu_init(0);
+
+	/* since we use random() in the wrappers, we need to seed it */
+	gettimeofday(&tv, NULL);
+	srandom(tv.tv_usec);
+
+	rec_dec();
+}
+
diff --git a/preload/posix/codegen.h b/preload/posix/codegen.h
new file mode 100644
index 0000000..bcae5d9
--- /dev/null
+++ b/preload/posix/codegen.h
@@ -0,0 +1,135 @@
+
+#ifndef _FIU_CODEGEN
+#define _FIU_CODEGEN
+
+#include <dlfcn.h>		/* dlsym() */
+#include <fiu.h>		/* fiu_* */
+#include <stdlib.h>		/* NULL, random() */
+
+/* Pointer to the dynamically loaded library */
+extern void *_fiu_libc;
+
+/* Recursion counter, per-thread */
+extern int __thread _fiu_called;
+
+/* Useful macros for recursion and debugging */
+#if 1
+	#define rec_inc() do { _fiu_called++; } while(0)
+	#define rec_dec() do { _fiu_called--; } while(0)
+	#define printd(...) do { } while(0)
+
+#else
+	/* debug variants */
+	#include <stdio.h>
+	#include <unistd.h>
+
+	#define rec_inc()				\
+		do {					\
+			_fiu_called++;			\
+			fprintf(stderr, "I: %d\n", _fiu_called); \
+			fflush(stderr);			\
+		} while (0)
+
+	#define rec_dec()				\
+		do {					\
+			_fiu_called--;			\
+			fprintf(stderr, "D: %d\n", _fiu_called); \
+			fflush(stderr);			\
+		} while (0)
+
+	#define printd(...)				\
+		do {					\
+			if (_fiu_called)		\
+				fprintf(stderr, "\t");	\
+			_fiu_called++;			\
+			fprintf(stderr, "%5.5d ", getpid()); \
+			fprintf(stderr, "%s(): ", __FUNCTION__ ); \
+			fprintf(stderr, __VA_ARGS__);	\
+			fflush(stderr);			\
+			_fiu_called--;			\
+		} while(0)
+#endif
+
+
+/*
+ * Wrapper generator macros
+ */
+
+/* Generates the common top of the wrapped function */
+#define mkwrap_top(RTYPE, NAME, PARAMS, PARAMSN, PARAMST)	\
+	static RTYPE (*_fiu_orig_##NAME) PARAMS = NULL;		\
+								\
+	static void __attribute__((constructor(201))) _fiu_init_##NAME(void) \
+	{							\
+		rec_inc();					\
+		_fiu_orig_##NAME = (RTYPE (*) PARAMST)		\
+				dlsym(_fiu_libc, #NAME);	\
+		rec_dec();					\
+	}							\
+								\
+	RTYPE NAME PARAMS					\
+	{ 							\
+		RTYPE r;					\
+		int fstatus;					\
+								\
+		if (_fiu_called) {				\
+			printd("orig\n");			\
+			return (*_fiu_orig_##NAME) PARAMSN;	\
+		}						\
+								\
+		printd("fiu\n");				\
+								\
+		/* fiu_fail() may call anything */		\
+		rec_inc();
+
+
+/* Generates the body of the function for normal, non-errno usage. The return
+ * value is taken from failinfo. */
+#define mkwrap_body_failinfo(FIU_NAME, RTYPE)			\
+								\
+		fstatus = fiu_fail(FIU_NAME);			\
+		if (fstatus != 0) {				\
+			r = (RTYPE) fiu_failinfo();		\
+			goto exit;				\
+		}
+
+/* Generates the body of the function for normal, non-errno usage. The return
+ * value is hardcoded. */
+#define mkwrap_body_hardcoded(FIU_NAME, FAIL_RET)		\
+								\
+		fstatus = fiu_fail(FIU_NAME);			\
+		if (fstatus != 0) {				\
+			r = FAIL_RET;				\
+			goto exit;				\
+		}
+
+/* Generates the body of the function for functions that affect errno. The
+ * return value is hardcoded. Assumes int valid_errnos[] exist was properly
+ * defined. */
+#define mkwrap_body_errno(FIU_NAME, FAIL_RET, NVERRNOS) \
+								\
+		fstatus = fiu_fail(FIU_NAME);			\
+		if (fstatus != 0) {				\
+			void *finfo = fiu_failinfo();		\
+			if (finfo == NULL) {			\
+				errno = valid_errnos[random() % NVERRNOS]; \
+			} else {				\
+				errno = (long) finfo;		\
+			}					\
+			r = FAIL_RET;				\
+			goto exit;				\
+		}
+
+
+#define mkwrap_bottom(NAME, PARAMSN)				\
+								\
+		r = (*_fiu_orig_##NAME) PARAMSN;		\
+								\
+	exit:							\
+		rec_dec();					\
+		return r;					\
+	}
+
+
+#endif /* _FIU_CODEGEN */
+
diff --git a/preload/posix/function_list.skel b/preload/posix/function_list.skel
new file mode 100644
index 0000000..d82bf59
--- /dev/null
+++ b/preload/posix/function_list.skel
@@ -0,0 +1,18 @@
+
+What follows is a list with the POSIX functions and their corresponding
+failure point names, as implemented in the preload library.
+
+It is not set in stone, although it shouldn't change frequently.
+
+
+Manually written
+----------------
+
+malloc				libc/mm/malloc
+realloc				libc/mm/realloc
+open				posix/io/oc/open
+
+
+Automatically generated
+-----------------------
+
diff --git a/preload/posix/generate b/preload/posix/generate
new file mode 100755
index 0000000..dc3e6f4
--- /dev/null
+++ b/preload/posix/generate
@@ -0,0 +1,293 @@
+#!/usr/bin/env python
+
+"""
+Reads function information and generates code for the preloader library.
+
+The code is NOT nice. It just does the trick.
+"""
+
+import sys
+import re
+
+
+# Function definition regular expression
+func_def_re = re.compile(
+	r'(?P<ret_type>(?:[\w\*]+\s+\**)+)+(?P<name>\w+).*\((?P<params>.*)\).*;')
+
+# Regular expression to extract the types and names of the parameters from a
+# string containing the definition parameters (e.g. from
+# "int a, const char *b" extracts [('int ', 'a'), ('const char *', 'b')]
+params_info_re = \
+	re.compile(r"(?:(?P<type>(?:[\w\*]+\s+\**)+)+(?P<name>\w+),?\s*)+?")
+
+
+class Context:
+	"""Represents the current context information within a module
+	definition file."""
+	def __init__(self):
+		self.fiu_name_base = 'UNKNOWN'
+
+
+class Function:
+	"Represents a function to be wrapped"
+
+	def __init__(self, definition, ctx):
+		"Constructor, takes the C definition as a string"
+		self.definition = definition
+		self.load_from_definition(definition)
+
+		# fiu name, constructed by default from the context but can be
+		# overriden by info
+		self.fiu_name = ctx.fiu_name_base + '/' + self.name
+
+		# what to return on error, by default set to None, which means
+		# "take it from failinfo"
+		self.on_error = None
+
+		# whether to set errno or not, and the list of valid errnos;
+		# in any case if failinfo is set we take the errno value from
+		# there
+		self.use_errno = False
+		self.valid_errnos = []
+
+	def load_from_definition(self, definition):
+		m = func_def_re.match(definition)
+		self.name = m.group("name")
+		self.ret_type = m.group("ret_type")
+		self.params =  m.group("params")
+		self.params_info = params_info_re.findall(self.params)
+
+	def load_info(self, info):
+		"Loads additional information from the given string"
+		if ':' in info:
+			s = info.split(':', 1)
+			k, v = s[0].strip(), s[1].strip()
+
+			if k == 'fiu name':
+				self.fiu_name = v
+			elif k == 'on error':
+				self.on_error = v
+			elif k == 'valid errnos':
+				self.use_errno = True
+				self.valid_errnos = v.split()
+			else:
+				raise SyntaxError, \
+					"Unknown information: " + k
+
+	def __repr__(self):
+		s = '<F %(rt)s %(n)s ( %(p)s ) -- %(fn)s %(oe)s %(ve)s>' % \
+			{
+				'rt': self.ret_type,
+				'n': self.name,
+				'p': self.params,
+				'fn': self.fiu_name,
+				'oe': self.on_error,
+				've': str(self.valid_errnos),
+			}
+		return s
+
+	def generate_to(self, f):
+		"""Generates code to the given file. Strongly related to
+		codegen.h."""
+		f.write('/* Wrapper for %s() */\n' % self.name)
+
+		# extract params names and types
+		paramst = ', '.join(i[0] for i in self.params_info)
+		paramsn = ', '.join(i[1] for i in self.params_info)
+
+		f.write('mkwrap_top(%s, %s, (%s), (%s), (%s))\n' % \
+				(self.ret_type, self.name, self.params,
+					paramsn, paramst) )
+
+		if self.use_errno:
+			if self.on_error is None:
+				desc = "%s uses errno but has no on_error" % \
+					self.name
+				raise RuntimeError, desc
+
+			# We can't put this as a macro parameter, so it has to
+			# be explicit 
+			valid_errnos = '{ ' + ', '.join(self.valid_errnos) \
+				+ ' }'
+			f.write("\tint valid_errnos[] = %s;\n" % valid_errnos)
+
+			f.write('mkwrap_body_errno("%s", %s, %d)\n' % \
+					(self.fiu_name, self.on_error,
+						len(self.valid_errnos)) )
+		elif self.on_error is not None:
+			f.write('mkwrap_body_hardcoded("%s", %s)\n' % \
+					(self.fiu_name, self.on_error) )
+		else:
+			f.write('mkwrap_body_failinfo("%s", %s)\n' % \
+					(self.fiu_name, self.ret_type) )
+
+		f.write('mkwrap_bottom(%s, (%s))\n' % (self.name, paramsn))
+		f.write('\n\n')
+
+
+class Include:
+	"Represents an include directive"
+	def __init__(self, path):
+		self.path = path
+
+	def __repr__(self):
+		return '<I %s>' % self.path
+
+	def generate_to(self, f):
+		f.write("#include %s\n" % self.path)
+
+class Verbatim:
+	"Represent a verbatim directive"
+	def __init__(self, line):
+		self.line = line
+
+	def __repr__(self):
+		return '<V %s>' % self.line
+
+	def generate_to(self, f):
+		f.write(self.line + '\n')
+
+class EmptyLine:
+	"Represents an empty line"
+	def __repr__(self):
+		return '<E>'
+
+	def generate_to(self, f):
+		f.write('\n')
+
+class Comment:
+	"Represents a full-line comment"
+	def __init__(self, line):
+		self.body = line.strip()[1:].strip()
+
+	def __repr__(self):
+		return '<C %s>' % self.body
+
+	def generate_to(self, f):
+		f.write("// %s \n" % self.body)
+
+
+def parse_module(path):
+	"Parses a module definition"
+
+	f = open(path)
+
+	directives = []
+	ctx = Context()
+	current_func = None
+
+	while True:
+		l = f.readline()
+
+		# handle EOF
+		if not l:
+			break
+
+		# handle \ at the end of the line
+		while l.endswith("\\\n"):
+			nl = f.readline()
+			l = l[:-2] + nl
+
+		if not l.strip():
+			directives.append(EmptyLine())
+			continue
+
+		if l.strip().startswith("#"):
+			directives.append(Comment(l))
+			continue
+
+
+		if not l.startswith(" ") and not l.startswith("\t"):
+			# either a new function or a directive, but in either
+			# case the current function is done
+			if current_func:
+				directives.append(current_func)
+			current_func = None
+
+			l = l.strip()
+
+			if ':' in l:
+				# directive
+				s = l.split(':', 1)
+				k, v = s[0].strip(), s[1].strip()
+				if k == 'fiu name base':
+					v = v.strip().strip('/')
+					ctx.fiu_name_base = v
+				elif k == 'include':
+					directives.append(Include(v))
+				elif k == 'v':
+					directives.append(Verbatim(v))
+				else:
+					raise SyntaxError, \
+						("Unknown directive", l)
+			else:
+				current_func = Function(l, ctx)
+		else:
+			# function information
+			current_func.load_info(l.strip())
+
+	if current_func:
+		directives.append(current_func)
+
+	return directives
+
+
+#
+# Code generation
+#
+
+# Templates
+
+gen_header = """
+/*
+ * AUTOGENERATED FILE - DO NOT EDIT
+ *
+ * This file was automatically generated by libfiu, do not edit it directly,
+ * but see libfiu's "preload" directory.
+ */
+
+#include "codegen.h"
+
+"""
+
+
+def generate_code(directives, path):
+	"""Generates code to the file in the given path"""
+	f = open(path, 'w')
+
+	f.write(gen_header)
+
+	for directive in directives:
+		directive.generate_to(f)
+
+
+def write_function_list(directives, path):
+	"Writes the function list to the given path"
+	f = open(path, 'a')
+	for d in directives:
+		if isinstance(d, Function):
+			f.write("%-32s%s\n" % (d.name, d.fiu_name))
+
+
+def usage():
+	print "Use: ./generate input.mod output.c file_list.fl"
+
+def main():
+	if len(sys.argv) < 4:
+		usage()
+		sys.exit(1)
+
+	input_name = sys.argv[1]
+	output_name = sys.argv[2]
+	filelist_name = sys.argv[3]
+
+	directives = parse_module(input_name)
+	#import pprint
+	#pprint.pprint(directives)
+
+	generate_code(directives, output_name)
+	write_function_list(directives, filelist_name)
+
+if __name__ == '__main__':
+	main()
+
diff --git a/preload/posix/modules/libc.mm.custom.c b/preload/posix/modules/libc.mm.custom.c
new file mode 100644
index 0000000..8e0210b
--- /dev/null
+++ b/preload/posix/modules/libc.mm.custom.c
@@ -0,0 +1,105 @@
+
+/*
+ * Custom-made wrappers for malloc() and realloc().
+ *
+ * They can't be made generic because, at least on glibc, they're used before
+ * constructors are called.
+ *
+ * We use __malloc_hook, the glibc-specific interface, so this is glibc-only.
+ */
+
+#include <features.h>
+
+#ifndef __GLIBC__
+  #warning "Not using glibc, so no malloc() wrappers will be available"
+#else
+
+#include "codegen.h"
+
+#include <malloc.h>
+
+
+/* Original glibc's hooks, saved in our init function */
+static void *(*old_malloc_hook)(size_t, const void *);
+static void *(*old_realloc_hook)(void *ptr, size_t size, const void *caller);
+
+/* Our own hooks, that will replace glibc's */
+static void *fiu_malloc_hook(size_t size, const void *caller);
+static void *fiu_realloc_hook(void *ptr, size_t size, const void *caller);
+
+
+/* We are not going to use __malloc_initialize_hook because it would be called
+ * before our library initialization functions, which include fiu_init().
+ * Instead, we will use a constructor just like the other wrappers, but it
+ * will run after them just to make things tidier (NOT because it is
+ * necessary). */
+
+static void __attribute__((constructor(202))) fiu_init_malloc(void)
+{
+	/* Save original hooks, used in ours to prevent unwanted recursion */
+	old_malloc_hook = __malloc_hook;
+	old_realloc_hook = __realloc_hook;
+
+	__malloc_hook = fiu_malloc_hook;
+	__realloc_hook = fiu_realloc_hook;
+}
+
+
+static void *fiu_malloc_hook(size_t size, const void *caller)
+{
+	void *r;
+	int fstatus;
+
+	/* fiu_fail() may call anything */
+	rec_inc();
+
+	/* See __malloc_hook(3) for details */
+	__malloc_hook = old_malloc_hook;
+
+	fstatus = fiu_fail("libc/mm/malloc");
+	if (fstatus != 0) {
+		r = NULL;
+		goto exit;
+	}
+
+	r = malloc(size);
+
+exit:
+	old_malloc_hook = __malloc_hook;
+	__malloc_hook = fiu_malloc_hook;
+
+	rec_dec();
+	return r;
+}
+
+
+static void *fiu_realloc_hook(void *ptr, size_t size, const void *caller)
+{
+	void *r;
+	int fstatus;
+
+	/* fiu_fail() may call anything */
+	rec_inc();
+
+	/* See __malloc_hook(3) for details */
+	__realloc_hook = old_realloc_hook;
+
+	fstatus = fiu_fail("libc/mm/realloc");
+	if (fstatus != 0) {
+		r = NULL;
+		goto exit;
+	}
+
+	r = realloc(ptr, size);
+
+exit:
+	old_realloc_hook = __realloc_hook;
+	__realloc_hook = fiu_realloc_hook;
+
+	rec_dec();
+	return r;
+}
+
+
+#endif // defined __GLIBC__
+
diff --git a/preload/posix/modules/linux.io.mod b/preload/posix/modules/linux.io.mod
new file mode 100644
index 0000000..e05df19
--- /dev/null
+++ b/preload/posix/modules/linux.io.mod
@@ -0,0 +1,16 @@
+
+include: <fcntl.h>
+include: <errno.h>
+
+fiu name base: linux/io/
+
+# sync_file_range() is linux-only
+v: #ifdef __linux__
+
+int sync_file_range(int fd, off_t offset, off_t nbytes, \
+		unsigned int flags);
+	on error: -1
+	valid errnos: EBADF EINVAL EIO ENOMEM ENOSPC
+
+v: #endif
+
diff --git a/preload/posix/modules/posix.custom.c b/preload/posix/modules/posix.custom.c
new file mode 100644
index 0000000..e85adee
--- /dev/null
+++ b/preload/posix/modules/posix.custom.c
@@ -0,0 +1,67 @@
+
+/*
+ * Custom-made wrappers for some special POSIX functions.
+ */
+
+#include "codegen.h"
+
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdarg.h>
+
+
+/* Wrapper for open(), we can't generate it because it has a variable number
+ * of arguments */
+static int (*_fiu_orig_open) (const char *pathname, int flags, ...) = NULL;
+
+static void __attribute__((constructor(201))) _fiu_init_open(void)
+{
+	rec_inc();
+	_fiu_orig_open = (int (*) (const char *, int, ...))
+			 dlsym(_fiu_libc, "open");
+	rec_dec();
+}
+
+int open(const char *pathname, int flags, ...)
+{
+	int r;
+	int fstatus;
+
+	/* Differences from the generated code begin here */
+
+	int mode;
+	va_list l;
+
+	if (flags & O_CREAT) {
+		va_start(l, flags);
+		mode = va_arg(l, mode_t);
+		va_end(l);
+	} else {
+		/* set it to 0, it's ignored anyway */
+		mode = 0;
+	}
+
+	if (_fiu_called) {
+		printd("orig\n");
+		return (*_fiu_orig_open) (pathname, flags, mode);
+	}
+
+	/* Differences from the generated code end here */
+
+	printd("fiu\n");
+
+	/* fiu_fail() may call anything */
+	rec_inc();
+
+	/* Use the normal macros to complete the function, now that we have a
+	 * set mode to something */
+
+	int valid_errnos[] = { EACCES, EFAULT, EFBIG, EOVERFLOW, ELOOP, EMFILE, ENAMETOOLONG, ENFILE, ENOENT, ENOMEM, ENOSPC, ENOTDIR, EROFS };
+mkwrap_body_errno("posix/io/oc/open", -1, 13)
+mkwrap_bottom(open, (pathname, flags, mode))
+
+
diff --git a/preload/posix/modules/posix.io.mod b/preload/posix/modules/posix.io.mod
new file mode 100644
index 0000000..23b99d4
--- /dev/null
+++ b/preload/posix/modules/posix.io.mod
@@ -0,0 +1,195 @@
+
+# Posix I/O
+
+include: <sys/types.h>
+include: <sys/uio.h>
+include: <unistd.h>
+include: <sys/socket.h>
+include: <sys/stat.h>
+include: <sys/select.h>
+include: <poll.h>
+include: <fcntl.h>
+include: <errno.h>
+
+fiu name base: posix/io/oc/
+
+# open() has its own custom wrapper
+
+int close(int fd);
+	on error: -1
+	valid errnos: EBADFD EINTR EIO
+
+
+fiu name base: posix/io/sync/
+
+int fsync(int fd);
+	on error: -1
+	valid errnos: EBADFD EIO EROFS EINVAL
+
+int fdatasync(int fd);
+	on error: -1
+	valid errnos: EBADFD EIO EROFS EINVAL
+
+
+fiu name base: posix/io/rw/
+
+ssize_t read(int fd, void *buf, size_t count);
+	on error: -1
+	valid errnos: EBADFD EFAULT EINTR EINVAL EIO EISDIR EOVERFLOW
+
+ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+	on error: -1
+	valid errnos: EBADFD EFAULT EINTR EINVAL EIO EISDIR EOVERFLOW
+
+ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
+	on error: -1
+	valid errnos: EBADFD EFAULT EINTR EINVAL EIO EISDIR EOVERFLOW
+
+
+ssize_t write(int fd, const void *buf, size_t count);
+	on error: -1
+	valid errnos: EBADFD EFAULT EFBIG EINTR EINVAL EIO ENOSPC
+
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
+	on error: -1
+	valid errnos: EBADFD EFAULT EFBIG EINTR EINVAL EIO ENOSPC \
+		EOVERFLOW
+
+ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
+	on error: -1
+	valid errnos: EBADFD EFAULT EFBIG EINTR EINVAL EIO ENOSPC
+
+
+fiu name base: posix/io/dir/
+
+include: <dirent.h>
+
+DIR *opendir(const char *name);
+	on error: NULL
+	valid errnos: EACCES EBADF EMFILE ENFILE ENOENT ENOMEM ENOTDIR
+
+DIR *fdopendir(int fd);
+	on error: NULL
+	valid errnos: EACCES EBADF EMFILE ENFILE ENOENT ENOMEM ENOTDIR
+
+struct dirent *readdir(DIR *dirp);
+	on error: NULL
+	valid errnos: EBADF
+
+int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
+	on error: 1
+
+int closedir(DIR *dirp);
+	on error: -1
+	valid errnos: EBADF
+
+
+fiu name base: posix/io/stat/
+
+include: <sys/types.h>
+include: <sys/stat.h>
+include: <unistd.h>
+
+int stat(const char *path, struct stat *buf);
+	on error: -1
+	valid errnos: EACCES EBADF EFAULT ELOOP ENAMETOOLONG ENOENT ENOMEM \
+		ENOTDIR EOVERFLOW
+
+int fstat(int fd, struct stat *buf);
+	on error: -1
+	valid errnos: EACCES EBADF EFAULT ELOOP ENAMETOOLONG ENOENT ENOMEM \
+		ENOTDIR EOVERFLOW
+
+int lstat(const char *path, struct stat *buf);
+	on error: -1
+	valid errnos: EACCES EBADF EFAULT ELOOP ENAMETOOLONG ENOENT ENOMEM \
+		ENOTDIR EOVERFLOW
+
+
+fiu name base: posix/io/net/
+
+int socket(int domain, int type, int protocol);
+	on error: -1
+	valid errnos: EAFNOSUPPORT EMFILE ENFILE EPROTONOSUPPORT EPROTOTYPE \
+		EACCES ENOBUFS ENOMEM
+
+int bind(int socket, const struct sockaddr *address, socklen_t address_len);
+	on error: -1
+	valid errnos: EADDRINUSE EADDRNOTAVAIL EAFNOSUPPORT EBADF EINVAL ENOTSOCK \
+		EOPNOTSUPP EACCES EDESTADDRREQ EIO ELOOP ENAMETOOLONG ENOENT \
+		ENOTDIR EROFS EACCES EINVAL EISCONN ELOOP ENAMETOOLONG \
+		ENOBUFS
+
+int listen(int socket, int backlog);
+	on error: -1
+	valid errnos: EBADF EDESTADDRREQ EINVAL ENOTSOCK EOPNOTSUPP EACCES EINVAL \
+		ENOBUFS
+
+int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);
+	on error: -1
+	valid errnos:  EAGAIN EBADF ECONNABORTED EINTR EINVAL EMFILE ENFILE \
+		ENOTSOCK EOPNOTSUPP ENOBUFS ENOMEM EPROTO
+
+int connect(int socket, const struct sockaddr *address, socklen_t address_len);
+	on error: -1
+	valid errnos:  EADDRNOTAVAIL EAFNOSUPPORT EALREADY EBADF ECONNREFUSED \
+		EINPROGRESS EINTR EISCONN ENETUNREACH ENOTSOCK EPROTOTYPE \
+		ETIMEDOUT EIO ELOOP ENAMETOOLONG ENOENT ENOTDIR EACCES \
+		EADDRINUSE ECONNRESET EHOSTUNREACH EINVAL ELOOP ENAMETOOLONG \
+		ENETDOWN ENOBUFS EOPNOTSUPP
+
+ssize_t recv(int socket, void *buffer, size_t length, int flags);
+	on error: -1
+	valid errnos:  EAGAIN EBADF ECONNRESET EINTR EINVAL ENOTCONN ENOTSOCK \
+		EOPNOTSUPP ETIMEDOUT EIO ENOBUFS ENOMEM
+
+ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len);
+	on error: -1
+	valid errnos:  EAGAIN EBADF ECONNRESET EINTR EINVAL ENOTCONN ENOTSOCK \
+		EOPNOTSUPP ETIMEDOUT EIO ENOBUFS ENOMEM
+
+ssize_t recvmsg(int socket, struct msghdr *message, int flags);
+	on error: -1
+	valid errnos:  EAGAIN EBADF ECONNRESET EINTR EINVAL EMSGSIZE ENOTCONN \
+		ENOTSOCK EOPNOTSUPP ETIMEDOUT EIO ENOBUFS ENOMEM
+
+ssize_t send(int socket, const void *buffer, size_t length, int flags);
+	on error: -1
+	valid errnos:  EAGAIN EBADF ECONNRESET EDESTADDRREQ EINTR EMSGSIZE \
+		ENOTCONN ENOTSOCK EOPNOTSUPP EPIPE EACCES EIO ENETDOWN \
+		ENETUNREACH ENOBUFS
+
+ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
+	on error: -1
+	valid errnos:  EAFNOSUPPORT EAGAIN EBADF ECONNRESET EINTR EMSGSIZE \
+		ENOTCONN ENOTSOCK EOPNOTSUPP EPIPE EIO ELOOP ENAMETOOLONG \
+		ENOENT ENOTDIR EACCES EDESTADDRREQ EHOSTUNREACH EINVAL EIO \
+		EISCONN ENETDOWN ENETUNREACH ENOBUFS ENOMEM ELOOP \
+		ENAMETOOLONG
+
+ssize_t sendmsg(int socket, const struct msghdr *message, int flags);
+	on error: -1
+	valid errnos:  EAGAIN EAFNOSUPPORT EBADF ECONNRESET EINTR EINVAL EMSGSIZE \
+		ENOTCONN ENOTSOCK EOPNOTSUPP EPIPE EIO ELOOP ENAMETOOLONG \
+		ENOENT ENOTDIR EACCES EDESTADDRREQ EHOSTUNREACH EIO EISCONN \
+		ENETDOWN ENETUNREACH ENOBUFS ENOMEM ELOOP ENAMETOOLONG
+
+int shutdown(int socket, int how);
+	on error: -1
+	valid errnos:  EBADF EINVAL ENOTCONN ENOTSOCK ENOBUFS
+
+int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, \
+		struct timeval *timeout);
+	on error: -1
+	valid errnos: EBADF EINTR EINVAL ENOMEM
+
+int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, \
+		const struct timespec *timeout, const sigset_t *sigmask);
+	on error: -1
+	valid errnos: EBADF EINTR EINVAL ENOMEM
+
+int poll(struct pollfd *fds, nfds_t nfds, int timeout);
+	on error: -1
+	valid errnos: EBADF EFAULT EINTR EINVAL ENOMEM
+
+
diff --git a/preload/posix/modules/posix.mm.mod b/preload/posix/modules/posix.mm.mod
new file mode 100644
index 0000000..443f816
--- /dev/null
+++ b/preload/posix/modules/posix.mm.mod
@@ -0,0 +1,68 @@
+
+include: <unistd.h>
+include: <errno.h>
+include: <sys/mman.h>
+
+fiu name base: posix/mm/
+
+
+v: #ifdef _POSIX_MAPPED_FILES
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd, \
+		off_t offset);
+	on error: MAP_FAILED
+	valid errnos: EACCES EAGAIN EBADF EINVAL ENFILE ENODEV ENOMEM EPERM \
+		ETXTBSY
+
+int munmap(void *addr, size_t length);
+	on error: 0
+	valid errnos: EACCES EAGAIN EBADF EINVAL ENFILE ENODEV ENOMEM EPERM \
+		ETXTBSY
+
+
+int msync(void *addr, size_t length, int flags);
+	on error: -1
+	valid errnos: EBUSY EINVAL ENOMEM
+
+int mprotect(void *addr, size_t len, int prot);
+	on error: -1
+	valid errnos: EACCES EINVAL ENOMEM
+
+int madvise(void *addr, size_t length, int advice);
+	on error: -1
+	valid errnos: EAGAIN EBADF EINVAL EIO ENOMEM
+
+v: #else
+v:   #warning "no mmap() (and friends) wrappers available"
+v: #endif
+
+
+v: #ifdef _POSIX_MEMLOCK_RANGE
+
+int mlock(const void *addr, size_t len);
+	on error: -1
+	valid errnos: ENOMEM EPERM EAGAIN EINVAL
+
+int munlock(const void *addr, size_t len);
+	on error: -1
+	valid errnos: ENOMEM EPERM EAGAIN EINVAL
+
+v: #else
+v:   #warning "no mlock()/munlock() wrappers available"
+v: #endif
+
+
+v: #ifdef _POSIX_MEMLOCK
+
+int mlockall(int flags);
+	on error: -1
+	valid errnos: ENOMEM EPERM EINVAL
+
+int munlockall(void);
+	on error: -1
+	valid errnos: ENOMEM EPERM
+
+v: #else
+v:   #warning "no mlockall()/munlockall() wrappers available"
+v: #endif
+
diff --git a/preload/posix/modules/posix.proc.mod b/preload/posix/modules/posix.proc.mod
new file mode 100644
index 0000000..1054513
--- /dev/null
+++ b/preload/posix/modules/posix.proc.mod
@@ -0,0 +1,41 @@
+
+include: <unistd.h>
+include: <errno.h>
+include: <sys/types.h>
+include: <sys/wait.h>
+include: <signal.h>
+
+fiu name base: posix/proc/
+
+pid_t fork(void);
+	on error: -1
+	valid errnos: EAGAIN ENOMEM
+
+pid_t wait(int *status);
+	on error: -1
+	valid errnos: ECHILD EINTR EINVAL
+
+pid_t waitpid(pid_t pid, int *status, int options);
+	on error: -1
+	valid errnos: ECHILD EINTR EINVAL
+
+int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
+	on error: -1
+	valid errnos: ECHILD EINTR EINVAL
+
+int kill(pid_t pid, int sig);
+	on error: -1
+	valid errnos: EINVAL EPERM ESRCH
+
+# We need to do this typedef because our parser is not smart enough to handle
+# the function definition without it
+v: typedef void (*sighandler_t)(int);
+sighandler_t signal(int signum, sighandler_t handler);
+	on error: SIG_ERR
+        valid errnos: EINVAL
+
+int sigaction(int signum, const struct sigaction *act, \
+		struct sigaction *oldact);
+	on error: -1
+	valid errnos: EFAULT EINVAL
+
diff --git a/preload/posix/utils/extract_from_man b/preload/posix/utils/extract_from_man
new file mode 100755
index 0000000..00b65f0
--- /dev/null
+++ b/preload/posix/utils/extract_from_man
@@ -0,0 +1,164 @@
+#!/usr/bin/env python
+# encoding: utf8
+
+"""
+Extracts information from a manpage (read from stdin) that can be useful to
+create modules for the code generator.
+
+Example usage:
+	man 3posix chmod | extract_from_man
+
+Or, in a loop:
+
+	rm -f gen.mod;
+	for f in chmod chown chdir; do
+		man 3posix $f | extract_from_man >> gen.mod;
+	done
+
+"""
+
+import sys
+import re
+
+
+def wrap(s, cols, indent = 1):
+	ns = ''
+	line = ''
+	for w in s.split():
+		if len(line + ' ' + w) > cols:
+			ns += line + ' \\\n' + '\t' * indent
+			line = w
+		else:
+			if line:
+				line += ' ' + w
+			else:
+				line = w
+
+	ns += line
+
+	return ns.rstrip()
+
+
+def extract_sections(f):
+	"Reads a manpage from the file, returns a dictionary of sections."
+	sec_name = ''
+	sec_data = ''
+	sections = {}
+
+	for l in f:
+		if not l.strip():
+			continue
+
+		if l.startswith((' ', '\t')):
+			sec_data += l
+		else:
+			sections[sec_name] = sec_data
+			sec_name = l.strip()
+			sec_data = ''
+
+	sections[sec_name] = sec_data
+
+	return sections
+
+def get_ret_on_error(sections):
+	"Tries to find out what the function returns on error."
+	if 'RETURN VALUE' not in sections:
+		return None
+
+	# remove spaces and newlines to make it easier detect the patterns
+	s = ' '.join(sections['RETURN VALUE'].split())
+	print s
+
+	# Note: the '(-|‐)' regexp matches both the normal minus sign ('-')
+	# and the UTF-8 hypen sign ('‐', or \xe2\x80\x90); sadly both usually
+	# look the same
+	regexps = [
+		r'On error,? (?P<ev>[-\w]+) is returned',
+		r'On error,? .* returns? (?P<ev>[-\w]+).',
+		r'some error occurs,? (?P<ev>[-\w]+) is returned',
+		r'and (?P<ev>[-\w]+) if an error occurr(s|ed)',
+		r'[Oo]ther((-|‐) )?wise, (?P<ev>[-\w]+) shall be returned',
+		r'Other((-|‐) )?wise, the functions shall return (?P<ev>[-\w]+) and'
+	]
+	regexps = map(re.compile, regexps)
+
+	possible_errors = []
+	for regexp in regexps:
+		m = regexp.search(s)
+		if m:
+			possible_errors.append(m.group('ev'))
+	return possible_errors
+
+def get_possible_errnos(sections):
+	"""Tries to find out the possible valid errno values after the
+	function has failed."""
+	if 'ERRORS' not in sections:
+		return None
+
+	errnos = []
+
+	for l in sections['ERRORS'].split('\n'):
+		m = re.match(r'\s+(?P<e>([A-Z]{3,},? *)+)\s*', l)
+		if m:
+			s = m.group('e').strip()
+			if not s:
+				continue
+
+			s = [ x.strip() for x in s.split(',') ]
+			errnos.extend(s)
+
+	return errnos
+
+def get_defs(sections):
+	"Tries to find out the includes and function definitions."
+	if 'SYNOPSIS' not in sections:
+		return None
+
+	includes = []
+	funcs = []
+
+	fre = re.compile(r'\s+(?P<f>[\w,\*\s]+\(?(\w|,|\*|\s|\.\.\.)*\)?[,;])$')
+
+	for l in sections['SYNOPSIS'].split('\n'):
+		sl = l.strip()
+		if sl.startswith('#include'):
+			includes.append(sl.split(' ', 1)[1])
+
+		m = fre.match(l.rstrip())
+		if m:
+			f = m.group('f')
+
+			# long functions are split in multiple lines, this
+			# tries to detect that and append to the last seen
+			# function
+			if funcs and not funcs[-1].endswith(';'):
+				funcs[-1] += ' ' + f
+			else:
+				funcs.append(f)
+	return (includes, funcs)
+
+
+if __name__ == '__main__':
+
+	if len(sys.argv) > 1:
+		print __doc__
+		sys.exit(1)
+
+	s = extract_sections(sys.stdin)
+	on_error = get_ret_on_error(s)
+	errnos = get_possible_errnos(s)
+	incs, funcs = get_defs(s)
+
+	print '\n'.join( 'include: ' + i for i in incs)
+	print
+
+	print '\n'.join(funcs)
+
+	if on_error:
+		print '\ton error:', ' || '.join(on_error)
+
+	if errnos:
+		print '\tvalid errnos:', wrap(' '.join(errnos), 60,
+				indent = 2)
+
+
diff --git a/preload/run/Makefile b/preload/run/Makefile
new file mode 100644
index 0000000..cb11220
--- /dev/null
+++ b/preload/run/Makefile
@@ -0,0 +1,57 @@
+
+CFLAGS += -std=c99 -Wall -O3
+ALL_CFLAGS = -D_XOPEN_SOURCE=500 -fPIC -DFIU_ENABLE=1 \
+		-I. -I../../libfiu/ -L../../libfiu/ $(CFLAGS)
+
+ifdef DEBUG
+ALL_CFLAGS += -g
+endif
+
+ifdef PROFILE
+ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage
+endif
+
+# prefix for installing the binaries
+PREFIX=/usr/local
+
+
+OBJS = run.o
+
+
+ifneq ($(V), 1)
+	NICE_CC = @echo "  CC  $@"; $(CC)
+else
+	NICE_CC = $(CC)
+endif
+
+
+default: all
+	
+all: fiu_run_preload.so fiu-run
+
+.c.o:
+	$(NICE_CC) $(ALL_CFLAGS) -c $< -o $@
+
+fiu_run_preload.so: $(OBJS)
+	$(NICE_CC) $(ALL_CFLAGS) -shared -fPIC $(OBJS) -lfiu -ldl \
+		-o fiu_run_preload.so
+
+fiu-run: fiu-run.in
+	cat fiu-run.in | sed "s+@@PREFIX@@+$(PREFIX)+g" > fiu-run
+	chmod +x fiu-run
+
+install: fiu_run_preload.so fiu-run
+	install -d $(PREFIX)/lib
+	install -m 0755 fiu_run_preload.so $(PREFIX)/lib
+	install -d $(PREFIX)/bin
+	install -m 0755 fiu-run $(PREFIX)/bin
+	install -d $(PREFIX)/man/man1
+	install -m 0644 fiu-run.1 $(PREFIX)/man/man1/
+
+clean:
+	rm -f $(OBJS) fiu_run_preload.so fiu-run
+	rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out
+
+.PHONY: default install clean
+
+
diff --git a/preload/run/fiu-run.1 b/preload/run/fiu-run.1
new file mode 100644
index 0000000..bcc9788
--- /dev/null
+++ b/preload/run/fiu-run.1
@@ -0,0 +1,80 @@
+.TH fiu-run 1 "16/Jun/2009"
+.SH NAME
+fiu-run - a script to launch programs using libfiu
+.SH SYNOPSIS
+fiu-run [options] program [program arguments]
+
+.SH DESCRIPTION
+fiu-run is a script to make it easier to launch programs using
+\fBlibfiu\fR(3). It can enable failure points and start libfiu's remote
+control capabilities before starting to execute the program, avoiding the need
+to write a special launcher to inject failures.
+
+It is specially useful when used to inject failures in the POSIX/libc
+functions, because it does not require any program modifications.
+
+After launching programs with fiu-run, \fBfiu-ctrl\fR(1) can be used to enable
+and disable their failure points at runtme.
+
+For additional documentation, go to the project's website at
+.IR http://blitiri.com.ar/p/libfiu .
+
+.SH OPTIONS
+.TP
+.B "-e fpname"
+Enable the given failure point name.
+.TP
+.B "-p prob"
+Use the given probability for the previous failure point. In percent, defaults
+to 100, which means "always enabled". Must come \fIafter\fR the \fB-e\fR it
+affects.
+.TP
+.B "-u failnum"
+Use the given number as the failnum for the previous failure point. Must be !=
+0, defaults to 1. Must come \fIafter\fR the \fB-e\fR it affects.
+.TP
+.B "-i failinfo"
+Use the given number as the failinfo for the previous failure point. Defaults
+to 0. Must come \fIafter\fR the \fB-e\fR it affects.
+.TP
+.B -x
+Use the POSIX libfiu preload library, allows simulate failures in the POSIX
+and C standard library functions.
+.TP
+.B "-f ctrlpath"
+Enable remote control over named pipes with the given path as base name, the
+process id will be appended (defaults to "$TMPDIR/fiu-ctrl", or
+"/tmp/fiu-ctrl" if "$TMPDIR" is not set). Set to "" to disable remote control
+over named pipes.
+.TP
+.B "-l path"
+Path where to find the libfiu preload libraries. Defaults to the path where
+they were installed, so it is usually correct.
+
+.P
+You can enable any number of failure points by using \fB-e\fR repeatedly.
+
+.SH EXAMPLES
+The following will run the \fBfortune\fR(1) program simulating faults in all
+the POSIX I/O functions with a 10% probability, and in malloc() with 5%
+probability (note that the \fB-x\fR parameter is required in this case to
+enable failure points in the POSIX and libc functions):
+
+.RS
+.nf
+fiu-run -x -e posix/io/* -p 10 -e libc/mm/malloc -p 5 fortune
+.fi
+.RE
+
+By running it multiple times you will see that sometimes it works, but most of
+the time you get different errors, resulting from the simulated failures.
+
+.SH SEE ALSO
+.BR libfiu (3),
+.BR fiu-ctrl (1).
+
+.SH BUGS
+If you want to report bugs, or have any questions or comments, just let me
+know at albertito@blitiri.com.ar. For more information about libfiu, you can
+go to http://blitiri.com.ar/p/libfiu.
+
diff --git a/preload/run/fiu-run.in b/preload/run/fiu-run.in
new file mode 100644
index 0000000..8b97267
--- /dev/null
+++ b/preload/run/fiu-run.in
@@ -0,0 +1,122 @@
+#!/bin/bash
+
+# This script aims to make the use of the fiu_run_preload library a little
+# easier by providing a more friendly user interface, and abstracting the
+# environment variables used to communicate with it.
+
+# default remote control over named pipes prefix
+FIFO_PREFIX="${TMPDIR:-/tmp}/fiu-ctrl"
+
+# default library path to look for preloader libraries
+PLIBPATH="@@PREFIX@@/lib"
+
+# the enable string to pass to the preload library (via the FIU_ENABLE
+# environment variable)
+ENABLE=""
+
+# additional preloader libraries to use
+PRELOAD_LIBS=""
+
+
+HELP_MSG="
+Usage: fiu-run [options] program [arguments]
+
+The following options are supported:
+
+  -e fpname	Enable the given failure point name.
+  -p prob	... with the given probability (defaults to 100%).
+  -u failnum	... and this failnum (must be != 0) (defaults to 1).
+  -i failinfo	... and this failinfo (defaults to 0).
+  -x		Use POSIX libfiu preload library, allows simulate failures in
+		the POSIX and C standard library functions.
+  -f ctrlpath	Enable remote control over named pipes with the given path as
+		base name, the process id will be appended (defaults to
+		\"$FIFO_PREFIX\", set to \"\" to disable).
+  -l path	Path where to find the libfiu preload libraries, defaults to
+		$PLIBPATH (which is usually correct).
+
+The -p, -u and -i options must come after the -e they affect.
+
+For example:
+
+  fiu-run -x -e 'posix/io/*' -p 25 -e libc/mm/malloc -p 5 ls -l
+
+will run \"ls -l\" enabling all failure points that begin with 'posix/io/'
+with a 25% of probability to fail, and the failure point libc/mm/malloc with a
+5% of probability to fail.
+"
+
+
+#
+# Parse the options
+#
+
+if [ $# -lt 1 ]; then
+	echo "$HELP_MSG"
+	exit 1
+fi
+
+function opts_reset() {
+	# variables to store what we know so far; after a new name is found
+	# the old one is added to $ENABLE
+	NAME=""
+	PROB=-1
+	FAILNUM=1
+	FAILINFO=0
+}
+
+opts_reset;
+while getopts "+e:p:u:i:f:l:xh" opt; do
+	case $opt in
+	e)
+		# add the current one, if any
+		if [ "$NAME" != "" ]; then
+			ENABLE="$ENABLE:$NAME,$PROB,$FAILNUM,$FAILINFO"
+			opts_reset;
+		fi
+		NAME="$OPTARG"
+		;;
+	p)
+		PROB="$OPTARG"
+		;;
+	u)
+		FAILNUM="$OPTARG"
+		;;
+	i)
+		FAILINFO="$OPTARG"
+		;;
+	f)
+		FIFO_PREFIX="$OPTARG"
+		;;
+	l)
+		PLIBPATH="$OPTARG"
+		;;
+	x)
+		PRELOAD_LIBS="$PRELOAD_LIBS $PLIBPATH/fiu_posix_preload.so"
+		;;
+	h|*)
+		echo "$HELP_MSG"
+		exit 1
+		;;
+	esac;
+done
+
+# add leftovers
+if [ "$NAME" != "" ]; then
+	ENABLE="$ENABLE:$NAME,$PROB,$FAILNUM,$FAILINFO"
+	opts_reset;
+fi
+
+# eat the parameters we already processed
+shift $(( $OPTIND - 1 ))
+
+
+#
+# Run the application
+#
+
+export FIU_ENABLE="$ENABLE"
+export FIU_CTRL_FIFO="$FIFO_PREFIX"
+export LD_PRELOAD="$PLIBPATH/fiu_run_preload.so $PRELOAD_LIBS"
+exec "$@"
+
diff --git a/preload/run/run.c b/preload/run/run.c
new file mode 100644
index 0000000..cf0dfa6
--- /dev/null
+++ b/preload/run/run.c
@@ -0,0 +1,129 @@
+
+#include <stdio.h>		/* printf() */
+#include <unistd.h>		/* execve() */
+#include <string.h>		/* strtok(), memset(), strncpy() */
+#include <stdlib.h>		/* atoi(), atol() */
+#include <getopt.h>		/* getopt() */
+
+#include <fiu.h>
+#include <fiu-control.h>
+
+/* Maximum size of parameters to the enable options */
+#define MAX_ENOPT 128
+
+enum fp_type {
+	FP_ALWAYS = 1,
+	FP_PROB = 2,
+};
+
+struct enable_option {
+	char buf[MAX_ENOPT];
+	enum fp_type type;
+	char *name;
+	int failnum;
+	unsigned long failinfo;
+	float probability;
+};
+
+/* Fills the enopt structure taking the values from the given string, which is
+ * assumed to be in of the form:
+ *
+ *   name,probability,failnum,failinfo
+ *
+ * All fields are optional, except for the name. On error, enopt->name will be
+ * set to NULL. Probability is in percent, -1 indicates it should always be
+ * enabled. */
+static void parse_enable(const char *s, struct enable_option *enopt)
+{
+	char *tok;
+	char *state;
+
+	enopt->name = NULL;
+	enopt->type = FP_ALWAYS;
+	enopt->failnum = 1;
+	enopt->failinfo = 0;
+	enopt->probability = 1;
+
+	memset(enopt->buf, 0, MAX_ENOPT);
+	strncpy(enopt->buf, s, MAX_ENOPT);
+
+	tok = strtok_r(enopt->buf, ",", &state);
+	if (tok == NULL)
+		return;
+	enopt->name = tok;
+
+	tok = strtok_r(NULL, ",", &state);
+	if (tok == NULL)
+		return;
+	if (atoi(tok) >= 0) {
+		enopt->type = FP_PROB;
+		enopt->probability = atof(tok) / 100.0;
+	}
+
+	tok = strtok_r(NULL, ",", &state);
+	if (tok == NULL)
+		return;
+	enopt->failnum = atoi(tok);
+
+	tok = strtok_r(NULL, ",", &state);
+	if (tok == NULL)
+		return;
+	enopt->failinfo = atol(tok);
+}
+
+/* We set the constructor with priority 300 so it runs after the other
+ * libfiu preloaders (if they're enabled), which use the 200 range */
+static void __attribute__((constructor(300))) fiu_run_init(void)
+{
+	int r;
+	struct enable_option enopt;
+	char *fiu_enable_env, *fiu_enable_s, *fiu_fifo_env;
+	char *tok, *state;
+
+	fiu_init(0);
+
+	fiu_fifo_env = getenv("FIU_CTRL_FIFO");
+	if (fiu_fifo_env != NULL && *fiu_fifo_env != '\0')
+		fiu_rc_fifo(fiu_fifo_env);
+
+	fiu_enable_env = getenv("FIU_ENABLE");
+	if (fiu_enable_env == NULL)
+		return;
+
+	/* copy fiu_enable_env to fiu_enable_s so we can strtok() it */
+	fiu_enable_s = malloc(strlen(fiu_enable_env) + 1);
+	strcpy(fiu_enable_s, fiu_enable_env);
+
+	state = NULL;
+	tok = strtok_r(fiu_enable_s, ":", &state);
+	while (tok != NULL) {
+		parse_enable(tok, &enopt);
+		if (enopt.name == NULL) {
+			fprintf(stderr, "fiu-run.so: "
+					"ignoring enable without name");
+			tok = strtok_r(NULL, ":", &state);
+			continue;
+		}
+
+		if (enopt.type == FP_ALWAYS) {
+			r = fiu_enable(enopt.name, enopt.failnum,
+					(void *) enopt.failinfo, 0);
+		} else {
+			r = fiu_enable_random(enopt.name, enopt.failnum,
+					(void *) enopt.failinfo, 0,
+					enopt.probability);
+		}
+
+		if (r < 0) {
+			fprintf(stderr, "fiu-run.so: "
+					"couldn't enable failure %s\n",
+					enopt.name);
+			continue;
+		}
+
+		tok = strtok_r(NULL, ":", &state);
+	}
+
+	free(fiu_enable_s);
+}
+
diff --git a/utils/Makefile b/utils/Makefile
new file mode 100644
index 0000000..a0ac66e
--- /dev/null
+++ b/utils/Makefile
@@ -0,0 +1,18 @@
+
+# prefix for installing the binaries
+PREFIX=/usr/local
+
+
+default:
+	
+install:
+	install -d $(PREFIX)/bin
+	install -m 0755 fiu-ctrl $(PREFIX)/bin
+	install -d $(PREFIX)/man/man1
+	install -m 0644 fiu-ctrl.1 $(PREFIX)/man/man1/
+
+clean:
+
+.PHONY: default install clean
+
+
diff --git a/utils/fiu-ctrl b/utils/fiu-ctrl
new file mode 100755
index 0000000..d2feeac
--- /dev/null
+++ b/utils/fiu-ctrl
@@ -0,0 +1,158 @@
+#!/bin/bash
+
+# This scripts present a friendly interface to the fiu remote control
+# capabilities. Currently, it supports only the named pipe method (the only
+# one implemented).
+
+# default remote control over named pipes prefix; we use the same one as
+# fiu-run so it's easier to use
+FIFO_PREFIX="${TMPDIR:-/tmp}/fiu-ctrl"
+
+# commands to send, will be filled by options processing; must be in the
+# format supported by the fiu remote control (see fiu-rc.c for more details)
+declare -a CMDS
+
+
+HELP_MSG="
+Usage: fiu-ctrl [options] PID [PID ...]
+
+The following options are supported:
+
+  -e fpname	Enable the given failure point name.
+  -p prob	... with the given probability (defaults to 100%).
+  -u failnum	... and this failnum (must be != 0) (defaults to 1).
+  -i failinfo	... and this failinfo (defaults to 0).
+  -d fpname	Disable the given failure point name.
+  -f ctrlpath	Set the default prefix for remote control over named pipes.
+		(defaults to \"$FIFO_PREFIX\", which is usually correct if
+		the program was run using fiu-run(1)).
+
+The -p, -u and -i options must come after the -e they affect.
+
+For example:
+
+  fiu-ctrl -e posix/io/read -p 25 -e libc/mm/malloc -p 5 12345
+
+will tell the process with pid 12345 to enable the failure point
+'posix/io/read' with a 25% of probability to fail, and the failure point
+'libc/mm/malloc' with a 5% of probability to fail. And:
+
+  fiu-ctrl -d posix/io/read 12345
+
+will tell the same process to disable the previously enabled failure point.
+
+You can control multiple processes at once by specifiying more than one
+process ID.
+"
+
+
+#
+# Parse the options
+#
+
+if [ $# -lt 1 ]; then
+	echo "$HELP_MSG"
+	exit 1
+fi
+
+function opts_reset() {
+	# variables to store what we know so far; after a new name is found
+	# the old one is added to $ENABLE
+	NAME=""
+	PROB=-1
+	FAILNUM=1
+	FAILINFO=0
+}
+
+function add_cmd() {
+	if [ "$NAME" != "" ]; then
+		if [ $PROB -ge 0 ]; then
+			CMDS+="enable_random $NAME $PROB $FAILNUM $FAILINFO"
+		else
+			CMDS+="enable $NAME $FAILNUM $FAILINFO"
+		fi
+		opts_reset;
+	fi
+}
+
+opts_reset;
+while getopts "+e:p:u:i:d:f:h" opt; do
+	case $opt in
+	e)
+		# add the current one, if any
+		add_cmd;
+		opts_reset;
+		NAME="$OPTARG"
+		;;
+	p)
+		PROB="$OPTARG"
+		;;
+	u)
+		FAILNUM="$OPTARG"
+		;;
+	i)
+		FAILINFO="$OPTARG"
+		;;
+	f)
+		FIFO_PREFIX="$OPTARG"
+		;;
+	d)
+		CMDS+="disable $OPTARG"
+		opts_reset;
+		;;
+	h|*)
+		echo "$HELP_MSG"
+		exit 1
+		;;
+	esac;
+done
+
+# add leftovers
+if [ "$NAME" != "" ]; then
+	add_cmd;
+fi
+
+# eat the parameters we already processed
+shift $(( $OPTIND - 1 ))
+
+PIDS=""
+PREFIXES=""
+
+for i in "$@"; do
+	if test -p "$i.out"; then
+		PREFIXES="$PREFIXES $i"
+	elif kill -0 $i > /dev/null 2> /dev/null && \
+			test -p	"$FIFO_PREFIX-$i.out"; then
+		PIDS="$PIDS $i"
+	else
+		echo "Error: unknown pid or named pipe $i, skipping"
+		echo "Note that options must come before the PID"
+	fi
+done
+
+#
+# Send the commands
+#
+
+function send_cmd_fifo() {
+	# $1 = complete fifo prefix
+	# $2+ = command to send
+	# echoes the reply
+	P=$1
+	shift 1
+	echo $@ > $P.in
+	R="`cat $P.out`"
+	if [ "$R" -eq -1 ]; then
+		echo "$P: Command returned error"
+	fi
+}
+
+for c in "${CMDS[@]}"; do
+	for i in $PIDS; do
+		send_cmd_fifo $FIFO_PREFIX-$i "$c"
+	done
+	for i in $PREFIXES; do
+		send_cmd_fifo $i "$c"
+	done
+done
+
diff --git a/utils/fiu-ctrl.1 b/utils/fiu-ctrl.1
new file mode 100644
index 0000000..ceafd6b
--- /dev/null
+++ b/utils/fiu-ctrl.1
@@ -0,0 +1,73 @@
+.TH fiu-ctrl 1 "16/Jun/2009"
+.SH NAME
+fiu-ctrl - a script to remote control programs using libfiu
+.SH SYNOPSIS
+fiu-ctrl [options] PID [PID ...]
+
+.SH DESCRIPTION
+fiu-ctrl is a script to enable/disable failure points in running programs that
+are using \fBlibfiu\fR(3).
+
+Programs are usually launched using \fBfiu-run\fR(1), which enables
+libfiu's remote control capabilities without the need to modify the
+program's code.
+
+For additional documentation, go to the project's website at
+.IR http://blitiri.com.ar/p/libfiu .
+
+.SH OPTIONS
+.TP
+.B "-e fpname"
+Enable the given failure point name.
+.TP
+.B "-p prob"
+Use the given probability for the previous failure point. In percent, defaults
+to 100, which means "always enabled". Must come \fIafter\fR the \fB-e\fR it
+affects.
+.TP
+.B "-u failnum"
+Use the given number as the failnum for the previous failure point. Must be !=
+0, defaults to 1. Must come \fIafter\fR the \fB-e\fR it affects.
+.TP
+.B "-i failinfo"
+Use the given number as the failinfo for the previous failure point. Defaults
+to 0. Must come \fIafter\fR the \fB-e\fR it affects.
+.TP
+.B -d
+Disable the given failure point name.
+.TP
+.B "-f ctrlpath"
+Set the default prefix for remote control over named pipes. Defaults to
+"$TMPDIR/fiu-ctrl", or "/tmp/fiu-ctrl" if "$TMPDIR" is not set, which is the
+usually correct for programs launched using \fBfiu-run\fR(1).
+
+
+.SH EXAMPLES
+The following command will tell the process running with PID 12345 to enable
+the failure point \fIposix/io/read\fR with a 25% of probability to fail, and the
+failure point \fIlibc/mm/malloc\fR with a 5% of probability to fail:
+
+.RS
+.nf
+fiu-ctrl -e posix/io/read -p 25 -e libc/mm/malloc -p 5 12345
+.fi
+.RE
+
+And the following will tell the same process to disable the previously enabled
+failure point \fIposix/io/read\fR:
+
+.RS
+.nf
+fiu-ctrl -d posix/io/read 12345
+.fi
+.RE
+
+.SH SEE ALSO
+.BR libfiu (3),
+.BR fiu-run (1).
+
+.SH BUGS
+If you want to report bugs, or have any questions or comments, just let me
+know at albertito@blitiri.com.ar. For more information about libfiu, you can
+go to http://blitiri.com.ar/p/libfiu.
+
