git » libfiu » commit c7e0b63

Add remote control capabilities

author Alberto Bertogli
2009-06-13 04:32:07 UTC
committer Alberto Bertogli
2009-06-13 04:33:00 UTC
parent 85c8c763f10da0bb43d7a2f94c9fb5f9340bb890

Add remote control capabilities

This patch introduces remote control capabilities via named pipes.

Signed-off-by: Alberto Bertogli <albertito@blitiri.com.ar>

bindings/python/fiu.py +6 -0
bindings/python/fiu_ll.c +10 -0
doc/remote_control.rst +42 -0
libfiu/Makefile +1 -1
libfiu/fiu-control.h +10 -1
libfiu/fiu-rc.c +265 -0
libfiu/libfiu.3 +6 -0

diff --git a/bindings/python/fiu.py b/bindings/python/fiu.py
index 822ad9b..c61eef9 100644
--- a/bindings/python/fiu.py
+++ b/bindings/python/fiu.py
@@ -72,4 +72,10 @@ def disable(name):
 	if r != 0:
 		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/python/fiu_ll.c b/bindings/python/fiu_ll.c
index c1f5844..a558f28 100644
--- a/bindings/python/fiu_ll.c
+++ b/bindings/python/fiu_ll.c
@@ -168,6 +168,15 @@ static PyObject *disable(PyObject *self, PyObject *args)
 	return PyLong_FromLong(fiu_disable(name));
 }
 
+static PyObject *rc_fifo(PyObject *self, PyObject *args)
+{
+	char *basename;
+
+	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 },
@@ -176,6 +185,7 @@ static PyMethodDef fiu_methods[] = {
 	{ "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 }
 };
 
diff --git a/doc/remote_control.rst b/doc/remote_control.rst
new file mode 100644
index 0000000..eec7c5d
--- /dev/null
+++ b/doc/remote_control.rst
@@ -0,0 +1,42 @@
+
+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 failure injection in libraries.
+
+
+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 failure point name (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..4b9af5c 100644
--- a/libfiu/Makefile
+++ b/libfiu/Makefile
@@ -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 cd2bff4..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 */
 
 
@@ -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..678a4b8
--- /dev/null
+++ b/libfiu/fiu-rc.c
@@ -0,0 +1,265 @@
+
+/*
+ * 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"
+
+
+/* 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;
+
+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");
+			break;
+		} 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;
+
+	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)
+		return -1;
+
+	if (mknod(npipe_path_out, S_IFIFO | 0600, 0) != 0) {
+		unlink(npipe_path_in);
+		return -1;
+	}
+
+	if (pthread_create(&thread, NULL, rc_fifo_thread, NULL) != 0) {
+		unlink(npipe_path_in);
+		unlink(npipe_path_out);
+		return -1;
+	}
+
+	atexit(fifo_atexit);
+
+	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/libfiu.3 b/libfiu/libfiu.3
index cd27daf..b75d3d2 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
@@ -162,6 +163,11 @@ Disables the given point of failure, undoing the actions of the
 .B fiu_enable*()
 functions.
 
+.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 BUGS
 
 If you want to report bugs, or have any questions or comments, just let me