author | Alberto Bertogli
<albertito@blitiri.com.ar> 2009-06-13 04:32:07 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2009-06-13 04:33:00 UTC |
parent | 85c8c763f10da0bb43d7a2f94c9fb5f9340bb890 |
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