[netfilter-cvslog] r3482 - in trunk/nfsim: core tools
rusty at netfilter.org
rusty at netfilter.org
Wed Dec 22 02:05:20 CET 2004
Author: rusty at netfilter.org
Date: 2004-12-22 02:05:19 +0100 (Wed, 22 Dec 2004)
New Revision: 3482
Modified:
trunk/nfsim/core/core.h
trunk/nfsim/core/failtest.c
trunk/nfsim/core/message.c
trunk/nfsim/core/message.h
trunk/nfsim/tools/info.c
trunk/nfsim/tools/iptables.c
Log:
Major rework of message system, to handle forking while talking to iptables:
Fix --failpattern: check suppress before failpattern, for init code.
fflush(stdout) before forking, otherwise duplicated output.
Add start_program/end_program/fork_other_program to message.c
Add should_i_fail() calls to copy_from_user and copy_to_user.
add_message() and replay_messages() to "fork" iptables (alternate: pass file descriptors, which would be faster).
wait_for_message() now wait_for_output(): returns output directly.
Add "info failpoints" to see how many things could fail.
Modified: trunk/nfsim/core/core.h
===================================================================
--- trunk/nfsim/core/core.h 2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/core/core.h 2004-12-22 01:05:19 UTC (rev 3482)
@@ -279,6 +279,7 @@
bool should_i_fail_once(const char *location);
bool get_failtest(void);
extern bool suppress_failtest;
+extern unsigned int failpoints;
/* Root for all kernel code allocations (so we check memory leaks) */
extern void *nfsim_tallocs;
Modified: trunk/nfsim/core/failtest.c
===================================================================
--- trunk/nfsim/core/failtest.c 2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/core/failtest.c 2004-12-22 01:05:19 UTC (rev 3482)
@@ -28,7 +28,8 @@
static bool failtest = false;
bool suppress_failtest;
-static int generations = 0;
+static unsigned int generations = 0;
+unsigned int failpoints = 0;
/* Failures pattern so far. */
static char *faillist;
@@ -119,6 +120,9 @@
{
char *p;
+ if (suppress_failtest)
+ return false;
+
if (failpattern) {
p = strstr(orig_failpattern ?: "", location);
if (p && p <= failpattern
@@ -142,12 +146,17 @@
pid_t child;
int status;
+ if (suppress_failtest)
+ return false;
+
if (failpattern)
return do_failpattern(func);
- if (!failtest || suppress_failtest)
+ failpoints++;
+ if (!failtest)
return false;
+ fflush(stdout);
child = fork();
if (child == -1)
barf_perror("fork failed for failtest!");
@@ -155,7 +164,9 @@
/* The child actually fails. The script will screw up at this
* point, but should not crash. */
if (child == 0) {
+ /* If we're talking to iptables, it has to fork too. */
faillist = talloc_asprintf_append(faillist, "[%s]F", func);
+ fork_other_program();
if (++generations > 128)
barf("Too deep recursion on failure path:\n%s",
faillist);
Modified: trunk/nfsim/core/message.c
===================================================================
--- trunk/nfsim/core/message.c 2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/core/message.c 2004-12-22 01:05:19 UTC (rev 3482)
@@ -28,11 +28,12 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/wait.h>
static int sock[2]; /* socket for messages. 0 = parent, 1 = child */
-static int send_userspace_message(struct nf_userspace_message *msg);
-static int handle_userspace_message(int fd);
+static void send_userspace_message(struct nf_userspace_message *msg);
+static void handle_userspace_message(void);
void message_init(void)
{
@@ -68,70 +69,207 @@
sigprocmask(SIG_UNBLOCK, &ss, NULL);
}
-int handle_userspace_message(int fd)
+struct message
{
+ struct list_head list;
+ bool outgoing;
+ struct nf_userspace_message msg;
+ char data[0];
+};
+
+struct program
+{
+ char *name;
+ struct list_head messages;
+ pid_t pid;
+ int fd;
+ int argc;
+ char *argv[0];
+};
+static struct program *currprog;
+
+void start_program(const char *name, int argc, char *argv[])
+{
+ int i, childfd[2];
+
+ assert(!currprog);
+ currprog = talloc_size(NULL, sizeof(*currprog)
+ + (argc+1)*sizeof(char *));
+
+ currprog->name = talloc_strdup(currprog, name);
+ currprog->argc = argc;
+ for (i = 0; i < argc; i++)
+ currprog->argv[i] = talloc_strdup(currprog, argv[i]);
+ currprog->argv[argc] = NULL;
+ INIT_LIST_HEAD(&currprog->messages);
+
+ if (pipe(childfd) != 0)
+ barf_perror("%s pipe", currprog->name);
+
+ fflush(stdout);
+ currprog->pid = fork();
+ switch (currprog->pid) {
+ case -1:
+ barf_perror("iptables fork");
+ case 0:
+ dup2(childfd[1], STDOUT_FILENO);
+ dup2(childfd[1], STDERR_FILENO);
+ if (setenv("LD_PRELOAD", "fakesockopt.so.1.0", 1))
+ barf("putenv failed");
+ execvp(currprog->name, currprog->argv);
+ fprintf(stderr, "Could not exec %s!\n", currprog->name);
+ exit(EXIT_FAILURE);
+ }
+
+ close(childfd[1]);
+ currprog->fd = childfd[0];
+}
+
+int end_program(const char *name)
+{
+ int status;
+
+ if (waitpid(currprog->pid, &status, 0) <= 0)
+ barf_perror("Waiting for child %s", name);
+
+ close(currprog->fd);
+ talloc_free(currprog);
+ currprog = NULL;
+
+ return status;
+}
+
+static void add_message(bool outgoing, struct nf_userspace_message *msg,
+ int datalen, const void *data)
+{
+ struct message *new;
+
+ assert(currprog);
+ new = talloc_size(currprog, sizeof(*new) + datalen);
+ new->outgoing = outgoing;
+ new->msg = *msg;
+ memcpy(new->data, data, datalen);
+ list_add_tail(&new->list, &currprog->messages);
+}
+
+static void replay_messages(struct program *old)
+{
+ struct message *i, *in;
+ int totlen;
+
+ list_for_each_entry(i, &old->messages, list) {
+ add_message(i->outgoing, &i->msg, i->msg.len, &i->msg+1);
+ totlen = sizeof(i->msg) + i->msg.len;
+ if (i->outgoing) {
+ write(sock[0], &i->msg, totlen);
+ continue;
+ }
+
+ in = talloc_size(NULL, sizeof(*in) + i->msg.len);
+ /* Read in two parts: can be written that way. */
+ if (read(sock[0], &in->msg, sizeof(in->msg)) != sizeof(in->msg)
+ || read(sock[0], in->data, i->msg.len) != i->msg.len)
+ barf_perror("read during replay");
+ if (memcmp(&in->msg, &i->msg, totlen) != 0)
+ barf_perror("replay turned out different");
+ talloc_free(in);
+ }
+}
+
+void handle_userspace_message(void)
+{
int len;
struct nf_userspace_message msg;
/* FIXME: msg.len? Presumable always 0? --RR */
- len = read(fd, &msg, sizeof(msg));
+ len = read(sock[0], &msg, sizeof(msg));
if (len != sizeof(msg))
- return -1;
+ barf_perror("reading userspace message");
+#if 0
+ fprintf(stderr, "nfsim: %i received %s\n",
+ getpid(),
+ msg.type == UM_SYSCALL ?
+ (msg.opcode == SYS_GETSOCKOPT ? "GETSOCKOPT" :
+ msg.opcode == SYS_SETSOCKOPT ? "SETSOCKOPT" : "SYS???") :
+ (msg.opcode == KOP_COPY_FROM_USER ? "COPY_FROM_USER" :
+ msg.opcode == KOP_COPY_TO_USER ? "COPY_TO_USER" : "KOP???"));
+#endif
+
if (msg.type == UM_SYSCALL) {
+ add_message(false, &msg, 0, NULL);
switch (msg.opcode) {
case SYS_GETSOCKOPT:
msg.retval = nf_getsockopt(NULL, msg.args[0],
msg.args[1], (char *)msg.args[2],
&msg.args[3]);
- write(fd, &msg, sizeof(struct nf_userspace_message) +
- msg.len);
+#if 0
+ fprintf(stderr, "nfsim: %i sent GETSOCKOPT %i\n",
+ getpid(), msg.retval);
+#endif
+ write(sock[0], &msg, sizeof(msg));
+ add_message(true, &msg, 0, NULL);
break;
case SYS_SETSOCKOPT:
msg.retval = nf_setsockopt(NULL, msg.args[0],
msg.args[1], (char *)msg.args[2],
msg.args[3]);
- write(fd, &msg, sizeof(struct nf_userspace_message) +
- msg.len);
+#if 0
+ fprintf(stderr, "nfsim: %i sent SETSOCKOPT %i\n",
+ getpid(), msg.retval);
+#endif
+ write(sock[0], &msg, sizeof(msg));
+ add_message(true, &msg, 0, NULL);
break;
default:
- fprintf(stderr, "Invalid syscall opcode %d\n",
- msg.opcode);
+ barf("Invalid syscall opcode %d\n", msg.opcode);
}
} else if (msg.type == UM_KERNELOP) {
switch (msg.opcode) {
case KOP_COPY_FROM_USER:
- if (complete_read(fd, (char *)msg.args[0],
+ if (complete_read(sock[0], (char *)msg.args[0],
msg.args[2]) != msg.args[2])
barf_perror("read");
+ add_message(false, &msg, msg.args[2],
+ (char *)msg.args[0]);
break;
case KOP_COPY_TO_USER:
+ add_message(false, &msg, 0, NULL);
/* copying has been done */
break;
default:
- fprintf(stderr, "Invalid kernelop opcode %d\n",
- msg.opcode);
+ barf("Invalid kernelop opcode %d\n", msg.opcode);
}
- } else {
- fprintf(stderr, "Unknown message type %d\n", msg.type);
- }
-
- return 0;
+ } else
+ barf("Unknown message type %d\n", msg.type);
}
-int send_userspace_message(struct nf_userspace_message *msg)
+void send_userspace_message(struct nf_userspace_message *msg)
{
+#if 0
+ fprintf(stderr, "nfsim: %i sent %s\n",
+ getpid(),
+ msg->type == UM_SYSCALL ?
+ (msg->opcode == SYS_GETSOCKOPT ? "GETSOCKOPT" :
+ msg->opcode == SYS_SETSOCKOPT ? "SETSOCKOPT" : "SYS???") :
+ (msg->opcode == KOP_COPY_FROM_USER ? "COPY_FROM_USER" :
+ msg->opcode == KOP_COPY_TO_USER ? "COPY_TO_USER" : "KOP??"));
+#endif
write(sock[0], msg, sizeof(struct nf_userspace_message) + msg->len);
+ add_message(true, msg, msg->len, msg+1);
/* expect a reply */
- return handle_userspace_message(sock[0]);
+ handle_userspace_message();
}
int copy_to_user(void *to, const void *from, unsigned long n)
{
struct nf_userspace_message *msg;
+ if (should_i_fail(__func__))
+ return n;
+
msg = talloc_zero_named_const(NULL,
sizeof(struct nf_userspace_message) + n, "copy_to_user");
@@ -159,6 +297,9 @@
{
struct nf_userspace_message msg;
+ if (should_i_fail(__func__))
+ return n;
+
memset(&msg, 0, sizeof(struct nf_userspace_message));
msg.type = UM_KERNELOP;
@@ -177,20 +318,43 @@
return 0;
}
-/* Loop accepting messages from fakesockopt: stop when message on extra_fd */
-void wait_for_message(int extra_fd)
+/* This child wants a fresh program to play with */
+void fork_other_program(void)
{
+ struct program *old;
+
+ /* Guarantee new socket for this child. */
+ message_cleanup();
+ message_init();
+
+ if (!currprog)
+ return;
+
+ old = currprog;
+ currprog = NULL;
+ start_program(old->name, old->argc, old->argv);
+ replay_messages(old);
+ talloc_free(old);
+}
+
+/* Loop accepting messages from fakesockopt. If child talks, return. */
+char *wait_for_output(void)
+{
fd_set fdset;
int retry = 0;
- do {
+ for (;;) {
int maxfd, sret;
FD_ZERO(&fdset);
- FD_SET(extra_fd, &fdset);
FD_SET(sock[0], &fdset);
- maxfd = max(extra_fd, sock[0]);
+ maxfd = sock[0];
+ if (currprog) {
+ FD_SET(currprog->fd, &fdset);
+ if (currprog->fd > maxfd)
+ maxfd = currprog->fd;
+ }
sret = select(maxfd + 1, &fdset, NULL, NULL, NULL);
if (sret < 0) {
if ((errno == EINTR || errno == EAGAIN)
@@ -200,6 +364,17 @@
}
if (FD_ISSET(sock[0], &fdset))
- handle_userspace_message(sock[0]);
- } while (!FD_ISSET(extra_fd, &fdset));
+ handle_userspace_message();
+
+ if (currprog && FD_ISSET(currprog->fd, &fdset)) {
+ char *output = talloc_size(NULL, 1024);
+ int ret = read(currprog->fd, output, 1023);
+ if (ret > 0) {
+ output[ret] = '\0';
+ return output;
+ }
+ talloc_free(output);
+ return NULL;
+ }
+ }
}
Modified: trunk/nfsim/core/message.h
===================================================================
--- trunk/nfsim/core/message.h 2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/core/message.h 2004-12-22 01:05:19 UTC (rev 3482)
@@ -31,5 +31,14 @@
int copy_to_user(void *to, const void *from, unsigned long n);
int copy_from_user(void *to, const void *from, unsigned long n);
-void wait_for_message(int extra_fd);
+/* Returns talloced output of child (if running). */
+char *wait_for_output(void);
+
+/* We want to fork: split other program */
+void fork_other_program(void);
+
+/* Start a program*/
+void start_program(const char *name, int argc, char *argv[]);
+int end_program(const char *name);
+
#endif /* __HAVE_MESSAGE_H */
Modified: trunk/nfsim/tools/info.c
===================================================================
--- trunk/nfsim/tools/info.c 2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/tools/info.c 2004-12-22 01:05:19 UTC (rev 3482)
@@ -121,12 +121,24 @@
<para>the contents of the kernel routing cache</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>failpoints</term>
+ <listitem>
+ <para>show how many failure points have been passed: with --failtest, each of these alternatives would be attempted.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</section>
*/
}
+static bool print_failpoints(int argc, char **argv)
+{
+ nfsim_log(LOG_ALWAYS, "failpoints: %u", failpoints);
+ return true;
+}
+
static bool info(int argc, char **argv)
{
if (argc == 1) {
@@ -145,6 +157,8 @@
return rcaches(argc, argv);
else if (streq(argv[0], "timers"))
return timers(argc, argv);
+ else if (streq(argv[0], "failpoints"))
+ return print_failpoints(argc, argv);
nfsim_log(LOG_ALWAYS, "Unknown info command: %s", argv[0]);
Modified: trunk/nfsim/tools/iptables.c
===================================================================
--- trunk/nfsim/tools/iptables.c 2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/tools/iptables.c 2004-12-22 01:05:19 UTC (rev 3482)
@@ -31,66 +31,29 @@
#include "utils.h"
#include "message.h"
-/* FIXME: We can't fork while we're talking to the external program.
- * The correct thing to do is to record the messages up to the point
- * of fork in failtest.c, do a new run_command() and replay the
- * messages, which effectively "forks" the external program.
- */
static bool run_command(int argc, char **argv)
{
- pid_t child;
int status;
- int childfd[2];
- char *prefix;
+ char *prefix, *name, *output;
char buf[4096] = { 0 };
- suppress_failtest = true;
+ prefix = getenv("NFSIM_IPTABLES_PREFIX");
+ if (prefix)
+ name = talloc_asprintf(NULL, "%s/%s", prefix, argv[0]);
+ else
+ name = talloc_strdup(NULL, argv[0]);
- if (pipe(childfd) != 0)
- barf_perror("%s pipe", argv[0]);
+ start_program(name, argc, argv);
- child = fork();
- switch (child) {
- case -1:
- barf_perror("iptables fork");
- case 0:
- dup2(childfd[1], STDOUT_FILENO);
- dup2(childfd[1], STDERR_FILENO);
- if (setenv("LD_PRELOAD", "fakesockopt.so.1.0", 1))
- barf("putenv failed");
- prefix = getenv("NFSIM_IPTABLES_PREFIX");
- if (prefix) {
- char *path = talloc_asprintf(NULL, "%s/%s", prefix,
- argv[0]);
- execv(path, argv);
- fprintf(stderr, "Could not exec %s!\n", path);
- talloc_free(path);
- } else {
- execvp(argv[0], argv);
- fprintf(stderr, "Could not exec %s!\n", argv[0]);
- }
- exit(EXIT_FAILURE);
- }
-
- close(childfd[1]);
- for (;;) {
- char input[1024];
- int ret;
+ while ((output = wait_for_output()) != NULL) {
/* Take output from iptables, and feed through logging. */
- wait_for_message(childfd[0]);
- ret = read(childfd[0], input, sizeof(input)-1);
- if (ret <= 0)
- break;
- input[ret] = '\0';
- nfsim_log_partial(LOG_USERSPACE, buf, sizeof(buf), "%s",input);
+ nfsim_log_partial(LOG_USERSPACE, buf, sizeof(buf), "%s",
+ output);
+ talloc_free(output);
}
- suppress_failtest = false;
+ status = end_program(name);
+ talloc_free(name);
- if (waitpid(child, &status, 0) <= 0)
- barf_perror("Waiting for %s child", argv[0]);
-
- close(childfd[0]);
-
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
return true;
return false;
More information about the netfilter-cvslog
mailing list