Add a facility for tracking open file descriptors.  Information about
still open files is dumped out exit.  Enabled using the --track-fds
switch.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2031 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/corecheck/tests/Makefile.am b/corecheck/tests/Makefile.am
index 89a3659..8d8ad6b 100644
--- a/corecheck/tests/Makefile.am
+++ b/corecheck/tests/Makefile.am
@@ -8,6 +8,15 @@
 
 EXTRA_DIST = $(noinst_SCRIPTS) \
 	erringfds.stderr.exp erringfds.stdout.exp erringfds.vgtest \
+	fdleak_cmsg.stderr.exp fdleak_cmsg.vgtest \
+	fdleak_creat.stderr.exp fdleak_creat.vgtest \
+	fdleak_dup.stderr.exp fdleak_dup.vgtest \
+	fdleak_dup2.stderr.exp fdleak_dup2.vgtest \
+	fdleak_fcntl.stderr.exp fdleak_fcntl.vgtest \
+	fdleak_ipv4.stderr.exp fdleak_ipv4.stdout.exp fdleak_ipv4.vgtest \
+	fdleak_open.stderr.exp fdleak_open.vgtest \
+	fdleak_pipe.stderr.exp fdleak_pipe.vgtest \
+	fdleak_socketpair.stderr.exp fdleak_socketpair.vgtest \
 	pth_atfork1.stderr.exp pth_atfork1.stdout.exp pth_atfork1.vgtest \
 	pth_cancel2.stderr.exp pth_cancel2.vgtest \
 	pth_cvsimple.stderr.exp pth_cvsimple.stdout.exp pth_cvsimple.vgtest \
@@ -21,7 +30,9 @@
 	vgprintf.stderr.exp vgprintf.stdout.exp vgprintf.vgtest
 
 check_PROGRAMS = \
-	erringfds sigkill res_search \
+	erringfds fdleak_cmsg fdleak_creat fdleak_dup fdleak_dup2 \
+	fdleak_fcntl fdleak_ipv4 fdleak_open fdleak_pipe \
+	fdleak_socketpair sigkill res_search \
 	pth_atfork1 pth_cancel2 pth_cvsimple pth_empty \
 	pth_exit pth_mutexspeed pth_once \
 	vgprintf
@@ -31,8 +42,17 @@
 
 # C ones
 erringfds_SOURCES 	= erringfds.c
-sigkill_SOURCES 	= sigkill.c
-vgprintf_SOURCES 	= vgprintf.c
+fdleak_cmsg_SOURCES	= fdleak_cmsg.c
+fdleak_creat_SOURCES	= fdleak_creat.c
+fdleak_dup_SOURCES	= fdleak_dup.c
+fdleak_dup2_SOURCES	= fdleak_dup2.c
+fdleak_fcntl_SOURCES	= fdleak_fcntl.c
+fdleak_ipv4_SOURCES	= fdleak_ipv4.c
+fdleak_open_SOURCES	= fdleak_open.c
+fdleak_pipe_SOURCES	= fdleak_pipe.c
+fdleak_socketpair_SOURCES = fdleak_socketpair.c
+sigkill_SOURCES		= sigkill.c
+vgprintf_SOURCES	= vgprintf.c
 
 # Pthread ones
 pth_atfork1_SOURCES	= pth_atfork1.c
diff --git a/corecheck/tests/fdleak_cmsg.c b/corecheck/tests/fdleak_cmsg.c
new file mode 100644
index 0000000..a6dccb1
--- /dev/null
+++ b/corecheck/tests/fdleak_cmsg.c
@@ -0,0 +1,182 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+char filea[24];
+char fileb[24];
+char sock[24];
+
+void
+server ()
+{
+   int s, fd1, fd2;
+   struct sockaddr_un addr;
+   
+   fd1 = open(filea, O_RDWR | O_CREAT | O_TRUNC, 0750);
+   if(fd1 == -1) {
+      perror("open");
+      exit(1);
+   }
+
+   fd2 = open(fileb, O_RDWR | O_CREAT | O_TRUNC, 0750);
+   if(fd2 == -1) {
+      perror("open");
+      exit(1);
+   }
+
+   s = socket(PF_UNIX, SOCK_STREAM, 0);
+   if(s == -1) {
+      perror("socket");
+      exit(1);
+   }
+
+   memset(&addr, 0, sizeof(addr));
+   addr.sun_family = AF_UNIX;
+   sprintf(addr.sun_path, sock);
+
+   unlink(addr.sun_path);
+   if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+      perror("bind");
+      exit(1);
+   }
+
+   if(listen(s, 5) == -1) {
+      perror("listen");
+      exit(1);
+   }
+
+   {
+      int x;
+      int baddrsize = 0;
+      struct sockaddr_un baddr;
+      struct msghdr msg = {NULL, 0, NULL, 0, 0, 0, 0};
+      struct cmsghdr *cmsg;
+      char buf[CMSG_SPACE(sizeof(int) * 2)];
+      struct iovec iov[1];
+
+      memset(&baddr, 0, sizeof(baddr));
+      x = accept(s, (struct sockaddr *)&baddr, &baddrsize);
+      if(x == -1) {
+         perror("accept");
+         exit(1);
+      }
+
+      msg.msg_control = buf;
+      msg.msg_controllen = sizeof(buf);
+      cmsg = CMSG_FIRSTHDR(&msg);
+      cmsg->cmsg_level = SOL_SOCKET;
+      cmsg->cmsg_type = SCM_RIGHTS;
+      cmsg->cmsg_len = CMSG_LEN(sizeof(int) * 2);
+      ((int *)CMSG_DATA(cmsg))[0] = fd1;
+      ((int *)CMSG_DATA(cmsg))[1] = fd2;
+
+      iov[0].iov_base = "hello";
+      iov[0].iov_len = 6;
+
+      msg.msg_iov = iov;
+      msg.msg_iovlen = 1;
+
+      if(sendmsg(x, &msg, 0) == -1) {
+         perror("sendmsg");
+         exit(1);
+      }
+   }
+}
+
+void
+client ()
+{
+   int s, fd1 = -1, fd2 = -1, size, count = 0, ret;
+   struct sockaddr_un addr;
+   struct iovec iov[1];
+   union {
+      struct cmsghdr cm;
+      char control[CMSG_SPACE(sizeof(int) * 2)];
+   } control_un;
+   struct msghdr msg = { NULL, 0, iov, 1, control_un.control,
+                         sizeof(control_un), 0 };
+   struct cmsghdr *cmsg = &control_un.cm;
+   char buf[1024];
+
+   iov[0].iov_base = buf;
+   iov[0].iov_len = sizeof(buf);
+
+   s = socket(PF_UNIX, SOCK_STREAM, 0);
+   if(s == -1) {
+      perror("socket");
+      exit(1);
+   }
+
+   addr.sun_family = AF_UNIX;
+   sprintf(addr.sun_path, sock);
+
+   do {
+     count++;
+     ret = connect(s, (struct sockaddr *)&addr, sizeof(addr));
+     if(ret == -1) sleep(1);
+   } while (count < 10 && ret == -1);
+
+   if(ret == -1) {
+      perror("connect");
+      exit(1);
+   }
+
+   if((size = recvmsg(s, &msg, 0)) == -1) {
+      perror("recvmsg");
+      exit(1);
+   }
+
+
+   cmsg = CMSG_FIRSTHDR(&msg);
+   while(cmsg) {
+      if(cmsg->cmsg_level == SOL_SOCKET &&
+         cmsg->cmsg_type == SCM_RIGHTS &&
+         cmsg->cmsg_len == CMSG_LEN(sizeof(int) * 2)) {
+         fd1 = ((int *)CMSG_DATA(cmsg))[0];
+         fd2 = ((int *)CMSG_DATA(cmsg))[1];
+      }
+
+      cmsg = CMSG_NXTHDR(&msg, cmsg);
+   }
+
+   if(fd1 != -1) write(fd1, "Yeah 1\n", 8);
+   if(fd2 != -1) write(fd2, "Yeah 2\n", 8);
+}
+
+
+int
+main (int argc, char **argv)
+{
+   int pid, status;
+
+   /*
+    * Fedora Core 1's Perl opens /dev/pts/2 as fd 10.  Let's close it
+    * now to get consistent results across different releases.
+    */
+
+   close(10);
+
+   pid = getpid();
+   sprintf(filea, "/tmp/data1.%d", pid);
+   sprintf(fileb, "/tmp/data2.%d", pid);
+   sprintf(sock, "/tmp/sock.%d", pid);
+
+   if((pid = fork()) == 0) {
+      server();
+      return 0;
+   }
+
+   client();
+
+   wait(&status);
+
+   unlink(filea);
+   unlink(fileb);
+   unlink(sock);
+   return 0;
+}
diff --git a/corecheck/tests/fdleak_cmsg.stderr.exp b/corecheck/tests/fdleak_cmsg.stderr.exp
new file mode 100644
index 0000000..b229587
--- /dev/null
+++ b/corecheck/tests/fdleak_cmsg.stderr.exp
@@ -0,0 +1,61 @@
+
+
+FILE DESCRIPTORS: 8 open at exit.
+Open AF_UNIX socket .: /tmp/sock
+   at 0x........: __libc_accept (...libc...)
+   by 0x........: main (fdleak_cmsg.c:170)
+
+Open AF_UNIX socket .: /tmp/sock
+   at 0x........: __socket (in /...libc...)
+   by 0x........: main (fdleak_cmsg.c:170)
+
+Open file descriptor .: /tmp/data2
+   at 0x........: __libc_open (...libc...)
+   by 0x........: main (fdleak_cmsg.c:170)
+
+Open file descriptor .: /tmp/data1
+   at 0x........: __libc_open (...libc...)
+   by 0x........: main (fdleak_cmsg.c:170)
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+
+FILE DESCRIPTORS: 7 open at exit.
+Open file descriptor .: /tmp/data2
+   at 0x........: __libc_recvmsg (...libc...)
+   by 0x........: main (fdleak_cmsg.c:174)
+
+Open file descriptor .: /tmp/data1
+   at 0x........: __libc_recvmsg (...libc...)
+   by 0x........: main (fdleak_cmsg.c:174)
+
+Open AF_UNIX socket .: <unknown>
+   at 0x........: __socket (in /...libc...)
+   by 0x........: main (fdleak_cmsg.c:174)
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_cmsg.vgtest b/corecheck/tests/fdleak_cmsg.vgtest
new file mode 100644
index 0000000..9dcc793
--- /dev/null
+++ b/corecheck/tests/fdleak_cmsg.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_cmsg
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_creat.c b/corecheck/tests/fdleak_creat.c
new file mode 100644
index 0000000..9a2aaf1
--- /dev/null
+++ b/corecheck/tests/fdleak_creat.c
@@ -0,0 +1,21 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+   char filename[24];
+   
+   /*
+    * Fedora Core 1's Perl opens /dev/pts/2 as fd 10.  Let's close it
+    * now to get consistent results across different releases.
+    */
+
+   close(10);
+
+   sprintf(filename, "/tmp/file.%d\n", getpid());
+   creat(filename, 0);
+   unlink(filename);
+   return 0;
+}
diff --git a/corecheck/tests/fdleak_creat.stderr.exp b/corecheck/tests/fdleak_creat.stderr.exp
new file mode 100644
index 0000000..adb93bd
--- /dev/null
+++ b/corecheck/tests/fdleak_creat.stderr.exp
@@ -0,0 +1,23 @@
+
+
+FILE DESCRIPTORS: 5 open at exit.
+Open file descriptor .: /tmp/file
+
+   at 0x........: __libc_creat (...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_creat.vgtest b/corecheck/tests/fdleak_creat.vgtest
new file mode 100644
index 0000000..40a58a5
--- /dev/null
+++ b/corecheck/tests/fdleak_creat.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_creat
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_dup.c b/corecheck/tests/fdleak_dup.c
new file mode 100644
index 0000000..db8a287
--- /dev/null
+++ b/corecheck/tests/fdleak_dup.c
@@ -0,0 +1,19 @@
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+   int s;
+
+   /*
+    * Fedora Core 1's Perl opens /dev/pts/2 as fd 10.  Let's close it
+    * now to get consistent results across different releases.
+    */
+
+   close(10);
+
+   s = open("/dev/null", O_RDONLY);
+   dup(s);
+   return 0;
+}
diff --git a/corecheck/tests/fdleak_dup.stderr.exp b/corecheck/tests/fdleak_dup.stderr.exp
new file mode 100644
index 0000000..3ba15a8
--- /dev/null
+++ b/corecheck/tests/fdleak_dup.stderr.exp
@@ -0,0 +1,27 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open file descriptor .: /dev/null
+   at 0x........: __dup (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: /dev/null
+   at 0x........: __libc_open (...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_dup.vgtest b/corecheck/tests/fdleak_dup.vgtest
new file mode 100644
index 0000000..818e6a6
--- /dev/null
+++ b/corecheck/tests/fdleak_dup.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_dup
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_dup2.c b/corecheck/tests/fdleak_dup2.c
new file mode 100644
index 0000000..bcc8ff4
--- /dev/null
+++ b/corecheck/tests/fdleak_dup2.c
@@ -0,0 +1,23 @@
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+   int s1;
+   int s2;
+
+   /*
+    * Fedora Core 1's Perl opens /dev/pts/2 as fd 10.  Let's close it
+    * now to get consistent results across different releases.
+    */
+
+   close(10);
+
+   s1 = open("/dev/null", O_RDONLY);
+   s2 = open("/dev/null", O_RDONLY);
+
+   dup2(s1, 20);
+   dup2(s1, s2);
+   return 0;
+}
diff --git a/corecheck/tests/fdleak_dup2.stderr.exp b/corecheck/tests/fdleak_dup2.stderr.exp
new file mode 100644
index 0000000..cd001c7
--- /dev/null
+++ b/corecheck/tests/fdleak_dup2.stderr.exp
@@ -0,0 +1,32 @@
+
+
+FILE DESCRIPTORS: 7 open at exit.
+Open file descriptor .: /dev/null
+   at 0x........: __dup2 (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: /dev/null
+   at 0x........: __dup2 (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: /dev/null
+   at 0x........: __libc_open (...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_dup2.vgtest b/corecheck/tests/fdleak_dup2.vgtest
new file mode 100644
index 0000000..9296361
--- /dev/null
+++ b/corecheck/tests/fdleak_dup2.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_dup2
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_fcntl.c b/corecheck/tests/fdleak_fcntl.c
new file mode 100644
index 0000000..e2ea4f4
--- /dev/null
+++ b/corecheck/tests/fdleak_fcntl.c
@@ -0,0 +1,20 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+   int s1;
+
+   /*
+    * Fedora Core 1's Perl opens /dev/pts/2 as fd 10.  Let's close it
+    * now to get consistent results across different releases.
+    */
+
+   close(10);
+
+   s1 = open("/dev/null", O_RDONLY);
+   if(fcntl(s1, F_DUPFD, s1) == -1) perror("fcntl");
+   return 0;
+}
diff --git a/corecheck/tests/fdleak_fcntl.stderr.exp b/corecheck/tests/fdleak_fcntl.stderr.exp
new file mode 100644
index 0000000..270d35b
--- /dev/null
+++ b/corecheck/tests/fdleak_fcntl.stderr.exp
@@ -0,0 +1,26 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open file descriptor .: /dev/null
+   at 0x........: __libc_fcntl (...libc...)
+   by 0x........: main (fdleak_fcntl.c:18)
+
+Open file descriptor .: /dev/null
+   at 0x........: __libc_open (...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_fcntl.vgtest b/corecheck/tests/fdleak_fcntl.vgtest
new file mode 100644
index 0000000..62dda6e
--- /dev/null
+++ b/corecheck/tests/fdleak_fcntl.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_fcntl
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_ipv4.c b/corecheck/tests/fdleak_ipv4.c
new file mode 100644
index 0000000..98d28ae
--- /dev/null
+++ b/corecheck/tests/fdleak_ipv4.c
@@ -0,0 +1,109 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+void
+server ()
+{
+   int s, x;
+   struct sockaddr_in baddr;
+   struct sockaddr_in addr;
+   int baddrsize = sizeof(baddr);
+   int one = 1;
+   
+   s = socket(PF_INET, SOCK_STREAM, 0);
+   if(s == -1) {
+      perror("socket");
+      exit(1);
+   }
+
+   setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
+   memset(&addr, 0, sizeof(addr));
+   addr.sin_family = AF_INET;
+   addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+   addr.sin_port = 12321;
+
+   if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+      perror("bind");
+      exit(1);
+   }
+
+   if(listen(s, 5) == -1) {
+      perror("listen");
+      exit(1);
+   }
+
+   memset(&baddr, 0, sizeof(baddr));
+   x = accept(s, (struct sockaddr *)&baddr, &baddrsize);
+   if(x == -1) {
+      perror("accept");
+      exit(1);
+   }
+
+   write(x, "hello", 6);
+}
+
+void
+client ()
+{
+   int s, count = 0, ret;
+   struct sockaddr_in addr;
+   char buf[1024];
+
+   s = socket(PF_INET, SOCK_STREAM, 0);
+   if(s == -1) {
+      perror("socket");
+      exit(1);
+   }
+
+   addr.sin_family = AF_INET;
+   addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+   addr.sin_port = 12321;
+
+   do {
+     count++;
+     ret = connect(s, (struct sockaddr *)&addr, sizeof(addr));
+     if(ret == -1) sleep(1);
+   } while (count < 10 && ret == -1);
+
+   if(ret == -1) {
+      perror("connect");
+      exit(1);
+   }
+
+   read(s, buf, sizeof(buf));
+
+   printf("%s\n", buf);
+}
+
+
+int
+main (int argc, char **argv)
+{
+   int pid, status;
+
+   /*
+    * Fedora Core 1's Perl opens /dev/pts/2 as fd 10.  Let's close it
+    * now to get consistent results across different releases.
+    */
+
+   close(10);
+
+   if((pid = fork()) == 0) {
+      server();
+      return 0;
+   }
+
+   client();
+
+   wait(&status);
+
+   return 0;
+}
diff --git a/corecheck/tests/fdleak_ipv4.stderr.exp b/corecheck/tests/fdleak_ipv4.stderr.exp
new file mode 100644
index 0000000..d8cb997
--- /dev/null
+++ b/corecheck/tests/fdleak_ipv4.stderr.exp
@@ -0,0 +1,45 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open AF_INET socket 4: 127.0.0.1:... <-> 127.0.0.1:...
+   at 0x........: __libc_accept (...libc...)
+   by 0x........: main (fdleak_ipv4.c:100)
+
+Open AF_INET socket 3: 127.0.0.1:... <-> unbound
+   at 0x........: __socket (in /...libc...)
+   by 0x........: main (fdleak_ipv4.c:100)
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+
+FILE DESCRIPTORS: 5 open at exit.
+Open AF_INET socket 3: 127.0.0.1:... <-> 127.0.0.1:...
+   at 0x........: __socket (in /...libc...)
+   by 0x........: main (fdleak_ipv4.c:104)
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_ipv4.stdout.exp b/corecheck/tests/fdleak_ipv4.stdout.exp
new file mode 100644
index 0000000..ce01362
--- /dev/null
+++ b/corecheck/tests/fdleak_ipv4.stdout.exp
@@ -0,0 +1 @@
+hello
diff --git a/corecheck/tests/fdleak_ipv4.vgtest b/corecheck/tests/fdleak_ipv4.vgtest
new file mode 100644
index 0000000..3000ccf
--- /dev/null
+++ b/corecheck/tests/fdleak_ipv4.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_ipv4
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_open.c b/corecheck/tests/fdleak_open.c
new file mode 100644
index 0000000..16830fe
--- /dev/null
+++ b/corecheck/tests/fdleak_open.c
@@ -0,0 +1,15 @@
+#include <fcntl.h>
+
+int
+main (int argc, char **argv)
+{
+   /*
+    * Fedora Core 1's Perl opens /dev/pts/2 as fd 10.  Let's close it
+    * now to get consistent results across different releases.
+    */
+
+   close(10);
+
+   open("/dev/null", O_RDONLY);
+   return 0;
+}
diff --git a/corecheck/tests/fdleak_open.stderr.exp b/corecheck/tests/fdleak_open.stderr.exp
new file mode 100644
index 0000000..782e99e
--- /dev/null
+++ b/corecheck/tests/fdleak_open.stderr.exp
@@ -0,0 +1,22 @@
+
+
+FILE DESCRIPTORS: 5 open at exit.
+Open file descriptor .: /dev/null
+   at 0x........: __libc_open (...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_open.vgtest b/corecheck/tests/fdleak_open.vgtest
new file mode 100644
index 0000000..5a803ba
--- /dev/null
+++ b/corecheck/tests/fdleak_open.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_open
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_pipe.c b/corecheck/tests/fdleak_pipe.c
new file mode 100644
index 0000000..6c2d566
--- /dev/null
+++ b/corecheck/tests/fdleak_pipe.c
@@ -0,0 +1,17 @@
+#include <unistd.h>
+
+int
+main (int argc, char **argv)
+{
+   int fds[2];
+
+   /*
+    * Fedora Core 1's Perl opens /dev/pts/2 as fd 10.  Let's close it
+    * now to get consistent results across different releases.
+    */
+
+   close(10);
+
+   pipe(fds);
+   return 0;
+}
diff --git a/corecheck/tests/fdleak_pipe.stderr.exp b/corecheck/tests/fdleak_pipe.stderr.exp
new file mode 100644
index 0000000..f31301c
--- /dev/null
+++ b/corecheck/tests/fdleak_pipe.stderr.exp
@@ -0,0 +1,27 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open file descriptor .:
+   at 0x........: __pipe (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .:
+   at 0x........: __pipe (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_pipe.vgtest b/corecheck/tests/fdleak_pipe.vgtest
new file mode 100644
index 0000000..540567f
--- /dev/null
+++ b/corecheck/tests/fdleak_pipe.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_pipe
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/fdleak_socketpair.c b/corecheck/tests/fdleak_socketpair.c
new file mode 100644
index 0000000..a8ddf9c
--- /dev/null
+++ b/corecheck/tests/fdleak_socketpair.c
@@ -0,0 +1,17 @@
+#include <sys/socket.h>
+
+int
+main (int argc, char **argv)
+{
+   int fds[2];
+
+   /*
+    * Fedora Core 1's Perl opens /dev/pts/2 as fd 10.  Let's close it
+    * now to get consistent results across different releases.
+    */
+
+   close(10);
+
+   socketpair(AF_UNIX, SOCK_STREAM, PF_UNIX, fds);
+   return 0;
+}
diff --git a/corecheck/tests/fdleak_socketpair.stderr.exp b/corecheck/tests/fdleak_socketpair.stderr.exp
new file mode 100644
index 0000000..4d86c11
--- /dev/null
+++ b/corecheck/tests/fdleak_socketpair.stderr.exp
@@ -0,0 +1,27 @@
+
+
+FILE DESCRIPTORS: 6 open at exit.
+Open AF_UNIX socket .: <unknown>
+   at 0x........: __socketpair (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open AF_UNIX socket .: <unknown>
+   at 0x........: __socketpair (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ...
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+Open file descriptor .: .
+   <inherited from parent>
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/corecheck/tests/fdleak_socketpair.vgtest b/corecheck/tests/fdleak_socketpair.vgtest
new file mode 100644
index 0000000..c737e80
--- /dev/null
+++ b/corecheck/tests/fdleak_socketpair.vgtest
@@ -0,0 +1,3 @@
+prog: fdleak_socketpair
+vgopts: --track-fds=yes
+stderr_filter: filter_fdleak
diff --git a/corecheck/tests/filter_fdleak b/corecheck/tests/filter_fdleak
new file mode 100755
index 0000000..cfb8096
--- /dev/null
+++ b/corecheck/tests/filter_fdleak
@@ -0,0 +1,26 @@
+#! /bin/sh
+
+dir=`dirname $0`
+
+$dir/../../tests/filter_stderr_basic                    |
+
+# Anonymise addresses
+$dir/../../tests/filter_addresses                       |
+
+# Anonymise line numbers in mac_replace_strmem.c
+sed "s/mac_replace_strmem.c:[0-9]\+/mac_replace_strmem.c:.../"  |
+
+$dir/../../tests/filter_test_paths                      |
+
+# Anonymise paths like "(in /foo/bar/libc-baz.so)"
+sed "s/(in \/.*libc.*)$/(in \/...libc...)/"             |
+
+# Anonymise paths like "__libc_start_main (../foo/bar/libc-quux.c:129)"
+sed "s/__libc_\(.*\) (.*)$/__libc_\1 (...libc...)/"	|
+
+sed s/"^Open AF_UNIX socket [0-9]\+: <unknown>/Open AF_UNIX socket .: <unknown>/" |
+sed s/"^Open \(AF_UNIX socket\|file descriptor\) [0-9]\+: \/dev\/null/Open \\1 .: \/dev\/null/" |
+sed s/"^Open \(AF_UNIX socket\|file descriptor\) [0-9]\+: \/tmp\/\(sock\|data1\|data2\|file\)\.[0-9]\+/Open \\1 .: \/tmp\/\\2/" |
+sed s/"^Open file descriptor [0-9]\+: .*/Open file descriptor .: ./" |
+sed s/"^Open file descriptor [0-9]\+:$/Open file descriptor .:/" |
+sed s/"127.0.0.1:[0-9]\+/127.0.0.1:.../g"
diff --git a/coregrind/docs/coregrind_core.html b/coregrind/docs/coregrind_core.html
index 6ae2802..6afcba2 100644
--- a/coregrind/docs/coregrind_core.html
+++ b/coregrind/docs/coregrind_core.html
@@ -654,6 +654,15 @@
       <code>-v</code> option which prints out all used suppression records.
       </li><br><p>
 
+  <li><code>--track-fds=no</code> [default]<br>
+      <code>--track-fds=yes</code>
+      <p>When enabled, Valgrind will print out a list of open file
+      descriptors on exit.  Along with each file descriptor, Valgrind
+      prints out a stack backtrace of where the file was opened and any
+      details relating to the file descriptor such as the file name or
+      socket details.
+      <br><p>
+
   <li><code>--gdb-attach=no</code> [default]<br>
       <code>--gdb-attach=yes</code>
       <p>When enabled, Valgrind will pause after every error shown,
diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h
index ecc0506..c6b49b3 100644
--- a/coregrind/vg_include.h
+++ b/coregrind/vg_include.h
@@ -257,6 +257,9 @@
 extern Bool  VG_(clo_lowlat_syscalls);
 extern Bool  VG_(clo_lowlat_signals);
 
+/* Track open file descriptors? */
+extern Bool  VG_(clo_track_fds);
+
 /* Should we run __libc_freeres at exit?  Sometimes causes crashes.
    Default: YES.  Note this is subservient to VG_(needs).libc_freeres;
    if the latter says False, then the setting of VG_(clo_weird_hacks)
@@ -1574,6 +1577,10 @@
 typedef void (*vg_atfork_t)(ThreadId);
 extern void VG_(atfork)(vg_atfork_t pre, vg_atfork_t parent, vg_atfork_t child);
 
+/* fd leakage calls. */
+extern void VG_(init_preopened_fds) ( void );
+extern void VG_(fd_stats) ( void );
+
 /* ---------------------------------------------------------------------
    Exports of vg_transtab.c
    ------------------------------------------------------------------ */
diff --git a/coregrind/vg_main.c b/coregrind/vg_main.c
index edea109..d11a426 100644
--- a/coregrind/vg_main.c
+++ b/coregrind/vg_main.c
@@ -557,6 +557,7 @@
 Int    VG_(clo_backtrace_size) = 4;
 Char*  VG_(clo_weird_hacks)    = NULL;
 Bool   VG_(clo_run_libc_freeres) = True;
+Bool   VG_(clo_track_fds)      = False;
 Bool   VG_(clo_chain_bb)       = True;
 Bool   VG_(clo_show_below_main) = False;
 
@@ -658,6 +659,8 @@
 "    --suppressions=<filename> suppress errors described in <filename>\n"
 "    --gen-suppressions=no|yes print suppressions for errors detected [no]\n"
 
+"    --track-fds=no|yes        Track open file descriptors? [no]\n"
+
 "    --gdb-attach=no|yes       start GDB when errors detected? [no]\n"
 "    --gdb-path=/path/to/gdb   path to the GDB to use [/usr/bin/gdb]\n"
 "    --input-fd=<number>       file descriptor for (gdb) input [0=stdin]\n"
@@ -999,6 +1002,11 @@
       else if (VG_CLO_STREQ(argv[i], "--run-libc-freeres=no"))
          VG_(clo_run_libc_freeres) = False;
 
+      else if (VG_CLO_STREQ(argv[i], "--track-fds=yes"))
+         VG_(clo_track_fds) = True;
+      else if (VG_CLO_STREQ(argv[i], "--track-fds=no"))
+         VG_(clo_track_fds) = False;
+
       else if (VG_CLO_STREQN(15, argv[i], "--sanity-level="))
          VG_(sanity_level) = (Int)VG_(atoll)(&argv[i][15]);
 
@@ -1601,6 +1609,10 @@
    /* Set up baseBlock offsets and copy the saved machine's state into it. */
    vg_init_baseBlock();
 
+   /* Search for file descriptors that are inherited from our parent. */
+   if (VG_(clo_track_fds))
+      VG_(init_preopened_fds)();
+
    /* Initialise the scheduler, and copy the client's state from
       baseBlock into VG_(threads)[1].  Must be before:
       - VG_(sigstartup_actions)()
@@ -1675,6 +1687,10 @@
         "Warning: pthread scheduler exited due to deadlock");
    }
 
+   /* Print out file descriptor summary and stats. */
+   if(VG_(clo_track_fds))
+      VG_(fd_stats)();
+
    if (VG_(needs).core_errors || VG_(needs).skin_errors)
       VG_(show_all_errors)();
 
diff --git a/coregrind/vg_mylibc.c b/coregrind/vg_mylibc.c
index 07144b2..3693d91 100644
--- a/coregrind/vg_mylibc.c
+++ b/coregrind/vg_mylibc.c
@@ -1204,6 +1204,15 @@
    return res;
 }
 
+Int VG_(lseek) ( Int fd, Long offset, Int whence)
+{
+   Int res;
+   /* res = lseek( fd, offset, whence ); */
+   res = VG_(do_syscall)(__NR_lseek, fd, (UInt)offset, whence);
+   if (VG_(is_kerror)(res)) res = -1;
+   return res;
+}
+
 Int VG_(stat) ( Char* file_name, struct vki_stat* buf )
 {
    Int res;
@@ -1289,6 +1298,37 @@
 }
 
 
+/* Support for getrlimit. */
+Int VG_(getrlimit) (Int resource, struct vki_rlimit *rlim)
+{
+   Int res;
+   /* res = getrlimit( resource, rlim ); */
+   res = VG_(do_syscall)(__NR_getrlimit, (UInt)resource, (UInt)rlim);
+   if(VG_(is_kerror)(res)) res = -1;
+   return res;
+}
+
+
+/* Support for getdents. */
+Int VG_(getdents) (UInt fd, struct vki_dirent *dirp, UInt count)
+{
+   Int res;
+   /* res = getdents( fd, dirp, count ); */
+   res = VG_(do_syscall)(__NR_getdents, fd, (UInt)dirp, count);
+   if (VG_(is_kerror)(res)) res = -1;
+   return res;
+}
+
+/* Support for a readlink.  */
+Int VG_(readlink) (Char* path, Char* buf, UInt bufsiz)
+{
+   Int res;
+   /* res = readlink( path, buf, bufsiz ); */
+   res = VG_(do_syscall)(__NR_readlink, (UInt)path, (UInt)buf, bufsiz);
+   if (VG_(is_kerror)(res)) res = -1;
+   return res;
+}
+
 /* You'd be amazed how many places need to know the current pid. */
 Int VG_(getpid) ( void )
 {
@@ -1618,9 +1658,12 @@
    /usr/src/linux-2.4.9-31 */
 
 /* kernel, ./include/linux/net.h */
-#define SYS_SOCKET     1               /* sys_socket(2)        */
-#define SYS_CONNECT    3               /* sys_connect(2)       */
-#define SYS_SEND       9               /* sys_send(2)          */
+#define SYS_SOCKET      1              /* sys_socket(2)        */
+#define SYS_CONNECT     3              /* sys_connect(2)       */
+#define SYS_GETSOCKNAME 6              /* sys_getsockname(2)   */
+#define SYS_GETPEERNAME 7              /* sys_getpeername(2)   */
+#define SYS_SEND        9              /* sys_send(2)          */
+#define SYS_GETSOCKOPT  15             /* sys_getsockopt(2)    */
 
 typedef UInt __u32;
 
@@ -1630,7 +1673,6 @@
 };
 
 /* kernel, include/linux/socket.h */
-typedef unsigned short  vki_sa_family_t;
 #define AF_INET                2       /* Internet IP Protocol        */
 #define MSG_NOSIGNAL           0x4000  /* Do not generate SIGPIPE */
 
@@ -1822,6 +1864,48 @@
    return res;
 }
 
+Int VG_(getsockname) ( Int sd, struct vki_sockaddr *name, Int *namelen)
+{
+   Int res;
+   UInt args[3];
+   args[0] = sd;
+   args[1] = (UInt)name;
+   args[2] = (UInt)namelen;
+   res = VG_(do_syscall)(__NR_socketcall, SYS_GETSOCKNAME, (UInt)&args);
+   if(VG_(is_kerror)(res))
+      res = -1;
+   return res;
+}
+
+Int VG_(getpeername) ( Int sd, struct vki_sockaddr *name, Int *namelen)
+{
+   Int res;
+   UInt args[3];
+   args[0] = sd;
+   args[1] = (UInt)name;
+   args[2] = (UInt)namelen;
+   res = VG_(do_syscall)(__NR_socketcall, SYS_GETPEERNAME, (UInt)&args);
+   if(VG_(is_kerror)(res))
+      res = -1;
+   return res;
+}
+
+Int VG_(getsockopt) ( Int sd, Int level, Int optname, void *optval,
+                      Int *optlen)
+{
+   Int res;
+   UInt args[5];
+   args[0] = sd;
+   args[1] = (UInt)level;
+   args[2] = (UInt)optname;
+   args[3] = (UInt)optval;
+   args[4] = (UInt)optlen;
+   res = VG_(do_syscall)(__NR_socketcall, SYS_GETSOCKOPT, (UInt)&args);
+   if(VG_(is_kerror)(res))
+      res = -1;
+   return res;
+}
+
 
 /*--------------------------------------------------------------------*/
 /*--- end                                              vg_mylibc.c ---*/
diff --git a/coregrind/vg_syscalls.c b/coregrind/vg_syscalls.c
index cebcef7..7f28d5c 100644
--- a/coregrind/vg_syscalls.c
+++ b/coregrind/vg_syscalls.c
@@ -257,6 +257,291 @@
       return False;
 }
 
+/* One of these is allocated for each open file descriptor.  */
+
+typedef struct OpenFd
+{
+   Int fd;                        /* The file descriptor */
+   Char *pathname;                /* NULL if not a regular file or unknown */
+   ExeContext *where;             /* NULL if inherited from parent */
+   struct OpenFd *next, *prev;
+} OpenFd;
+
+/* List of allocated file descriptors. */
+
+static OpenFd *allocated_fds;
+
+/* Count of open file descriptors. */
+
+static int fd_count = 0;
+
+/* Given a file descriptor, attempt to deduce it's filename.  To do this,
+   we use /proc/self/fd/<FD>.  If this doesn't point to a file, or if it
+   doesn't exist, we just return NULL.  Otherwise, we return a pointer
+   to the file name, which the caller is responsible for freeing. */
+
+static
+Char *resolve_fname(Int fd)
+{
+   char tmp[28], buf[PATH_MAX];
+
+   VG_(sprintf)(tmp, "/proc/self/fd/%d", fd);
+   VG_(memset)(buf, 0, PATH_MAX);
+
+   if(VG_(readlink)(tmp, buf, PATH_MAX) == -1)
+      return NULL;
+
+   return ((buf[0] == '/') ? VG_(strdup)(buf) : NULL);
+}
+
+
+/* Note the fact that a file descriptor was just closed. */
+
+static
+void record_fd_close(Int tid, Int fd)
+{
+   OpenFd *i = allocated_fds;
+
+   while(i) {
+      if(i->fd == fd) {
+         if(i->prev)
+            i->prev->next = i->next;
+         else
+            allocated_fds = i->next;
+         if(i->next)
+            i->next->prev = i->prev;
+         if(i->pathname) 
+            VG_(free) (i->pathname);
+         VG_(free) (i);
+         fd_count--;
+         break;
+      }
+      i = i->next;
+   }
+}
+
+/* Note the fact that a file descriptor was just opened.  If the
+   tid is -1, this indicates an inherited fd.  If the pathname is NULL,
+   this either indicates a non-standard file (i.e. a pipe or socket or
+   some such thing) or that we don't know the filename.  If the fd is
+   already open, then we're probably doing a dup2() to an existing fd,
+   so just overwrite the existing one. */
+
+static
+void record_fd_open(Int tid, Int fd, char *pathname)
+{
+   OpenFd *i;
+
+   /* Check to see if this fd is already open. */
+   i = allocated_fds;
+   while (i) {
+      if (i->fd == fd) {
+         if (i->pathname) VG_(free)(i->pathname);
+         break;
+      }
+      i = i->next;
+   }
+
+   /* Not already one: allocate an OpenFd */
+   if (i == NULL) {
+      i = VG_(malloc)(sizeof(OpenFd));
+
+      i->prev = NULL;
+      i->next = allocated_fds;
+      if(allocated_fds) allocated_fds->prev = i;
+      allocated_fds = i;
+      fd_count++;
+   }
+
+   i->fd = fd;
+   i->pathname = pathname;
+   i->where = (tid == -1) ? NULL : VG_(get_ExeContext)(tid);
+}
+
+static
+Char *unix2name(struct sockaddr_un *sa, UInt len, Char *name)
+{
+   if(sa == NULL || len == 0 || sa->sun_path[0] == '\0') {
+      VG_(sprintf)(name, "<unknown>");
+   } else {
+      VG_(sprintf)(name, "%s", sa->sun_path);
+   }
+
+   return name;
+}
+
+static
+Char *inet2name(struct sockaddr_in *sa, UInt len, Char *name)
+{
+   if(sa == NULL || len == 0) {
+      VG_(sprintf)(name, "<unknown>");
+   } else {
+      UInt addr = sa->sin_addr.s_addr;
+
+      if (addr == 0) {
+         VG_(sprintf)(name, "<unbound>");
+      } else {
+         VG_(sprintf)(name, "%u.%u.%u.%u:%u",
+                      addr & 0xFF, (addr>>8) & 0xFF,
+                      (addr>>16) & 0xFF, (addr>>24) & 0xFF,
+                      sa->sin_port);
+      }
+   }
+
+   return name;
+}
+
+
+/*
+ * Try get some details about a socket.
+ */
+
+static void
+getsockdetails(int fd)
+{
+   union u {
+      struct sockaddr a;
+      struct sockaddr_in in;
+      struct sockaddr_un un;
+   } laddr;
+   socklen_t llen;
+
+   llen = sizeof(laddr);
+   VG_(memset)(&laddr, 0, llen);
+
+   if(VG_(getsockname)(fd, (struct vki_sockaddr *)&(laddr.a), &llen) != -1) {
+      switch(laddr.a.sa_family) {
+      case AF_INET: {
+         static char lname[32];
+         static char pname[32];
+         struct sockaddr_in paddr;
+         socklen_t plen = sizeof(struct sockaddr_in);
+
+         if(VG_(getpeername)(fd, (struct vki_sockaddr *)&paddr, &plen) != -1) {
+            VG_(message)(Vg_UserMsg, "Open AF_INET socket %d: %s <-> %s", fd,
+                         inet2name(&(laddr.in), llen, lname),
+                         inet2name(&paddr, plen, pname));
+         } else {
+            VG_(message)(Vg_UserMsg, "Open AF_INET socket %d: %s <-> unbound",
+                         fd, inet2name(&(laddr.in), llen, lname));
+         }
+         return;
+         }
+      case AF_UNIX: {
+         static char lname[256];
+         VG_(message)(Vg_UserMsg, "Open AF_UNIX socket %d: %s", fd,
+                      unix2name(&(laddr.un), llen, lname));
+         return;
+         }
+      default:
+         VG_(message)(Vg_UserMsg, "Open pf-%d socket %d:",
+                      laddr.a.sa_family, fd);
+         return;
+      }
+   }
+
+   VG_(message)(Vg_UserMsg, "Open socket %d:", fd);
+}
+
+
+/* Dump out a summary, and optionally a more detailed list, of open file
+   descriptors. */
+
+void VG_(fd_stats) ()
+{
+   OpenFd *i = allocated_fds;
+
+   VG_(message)(Vg_UserMsg,
+                "FILE DESCRIPTORS: %d open at exit.", fd_count);
+
+   while(i) {
+      if(i->pathname) {
+         VG_(message)(Vg_UserMsg, "Open file descriptor %d: %s", i->fd,
+                      i->pathname);
+      } else {
+         int val;
+         socklen_t len = sizeof(val);
+
+         if(VG_(getsockopt)(i->fd, SOL_SOCKET, SO_TYPE, &val, &len) == -1) {
+            VG_(message)(Vg_UserMsg, "Open file descriptor %d:", i->fd);
+         } else {
+            getsockdetails(i->fd);
+         }
+      }
+
+      if(i->where) {
+         VG_(pp_ExeContext)(i->where);
+         VG_(message)(Vg_UserMsg, "");
+      } else {
+         VG_(message)(Vg_UserMsg, "   <inherited from parent>");
+         VG_(message)(Vg_UserMsg, "");
+      }
+
+      i = i->next;
+   }
+
+   VG_(message)(Vg_UserMsg, "");
+}
+
+/* If /proc/self/fd doesn't exist for some weird reason (like you've
+   got a kernel that doesn't have /proc support compiled in), then we
+   need to find out what file descriptors we inherited from our parent
+   process the hard way - by checking each fd in turn. */
+
+static
+void do_hacky_preopened()
+{
+   struct vki_rlimit lim;
+   unsigned int count;
+   int i;
+
+   if(VG_(getrlimit) (VKI_RLIMIT_NOFILE, &lim) == -1) {
+      /* Hmm.  getrlimit() failed.  Now we're screwed, so just choose
+         an arbitrarily high number.  1024 happens to be the limit in
+         the 2.4 kernels. */
+      count = 1024;
+   } else {
+      count = lim.rlim_cur;
+   }
+
+   for (i = 0; i < count; i++)
+      if(VG_(fcntl)(i, VKI_F_GETFL, 0) != -1)
+         record_fd_open(-1, i, NULL);
+}
+
+/* Initialize the list of open file descriptors with the file descriptors
+   we inherited from out parent process. */
+
+void VG_(init_preopened_fds)()
+{
+   int f, ret;
+   struct vki_dirent d;
+
+   f = VG_(open)("/proc/self/fd", VKI_O_RDONLY, 0);
+   if(f == -1) {
+      do_hacky_preopened();
+      return;
+   }
+
+   while((ret = VG_(getdents)(f, &d, sizeof(d))) != 0) {
+      if(ret == -1)
+         goto out;
+
+      if(VG_(strcmp)(d.d_name, ".") && VG_(strcmp)(d.d_name, "..")) {
+         int fno = VG_(atoll)(d.d_name);
+
+         if(fno != f)
+            if(VG_(clo_track_fds))
+               record_fd_open(-1, fno, resolve_fname(fno));
+      }
+
+      VG_(lseek)(f, d.d_off, VKI_SEEK_SET);
+   }
+
+out:
+   VG_(close)(f);
+}
+
 static
 UInt get_shm_size ( Int shmid )
 {
@@ -341,6 +626,27 @@
                      (Addr)msg->msg_control, msg->msg_controllen );
 }
 
+void check_cmsg_for_fds(Int tid, struct msghdr *msg)
+{
+   struct cmsghdr *cm = CMSG_FIRSTHDR(msg);
+
+   while (cm) {
+      if (cm->cmsg_level == SOL_SOCKET &&
+          cm->cmsg_type == SCM_RIGHTS ) {
+         int *fds = (int *) CMSG_DATA(cm);
+         int fdc = (cm->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)))
+                         / sizeof(int);
+         int i;
+
+         for (i = 0; i < fdc; i++)
+            if(VG_(clo_track_fds))
+               record_fd_open (tid, fds[i], resolve_fname(fds[i]));
+      }
+
+      cm = CMSG_NXTHDR(msg, cm);
+   }
+}
+
 static
 void pre_mem_read_sockaddr ( ThreadId tid,
 			     Char *description,
@@ -1369,6 +1675,10 @@
       res = -VKI_EBADF;
 }
 
+POST(close)
+{
+   if(VG_(clo_track_fds)) record_fd_close(tid, arg1);
+}
 
 PRE(dup)
 {
@@ -1382,6 +1692,9 @@
    if (!fd_allowed(res, "dup", tid)) {
       VG_(close)(res);
       res = -VKI_EMFILE;
+   } else {
+      if(VG_(clo_track_fds))
+         record_fd_open(tid, res, resolve_fname(res));
    }
 }
 
@@ -1398,6 +1711,8 @@
    MAYBE_PRINTF("SYSCALL[%d]       dup2 ( %d, %d ) = %d\n", 
 		VG_(getpid)(), 
 		arg1, arg2, res);
+   if(VG_(clo_track_fds))
+      record_fd_open(tid, res, resolve_fname(res));
 }
 
 PRE(fcntl)
@@ -1406,6 +1721,13 @@
    MAYBE_PRINTF("fcntl ( %d, %d, %d )\n",arg1,arg2,arg3);
 }
 
+POST(fcntl)
+{
+   if (arg2 == VKI_F_DUPFD)
+      if(VG_(clo_track_fds))
+         record_fd_open(tid, res, resolve_fname(res));
+}
+
 PRE(fchdir)
 {
    /* int fchdir(int fd); */
@@ -1428,9 +1750,15 @@
 
 PRE(fcntl64)
 {
-   /* I don't know what the prototype for this is supposed to be. */
-   /* ??? int fcntl(int fd, int cmd); */
-   MAYBE_PRINTF("fcntl64 (?!) ( %d, %d )\n", arg1,arg2);
+   /* int fcntl64(int fd, int cmd, int arg); */
+   MAYBE_PRINTF("fcntl64 ( %d, %d, %d )\n", arg1,arg2,arg3);
+}
+
+POST(fcntl64)
+{
+   if (arg2 == VKI_F_DUPFD)
+      if(VG_(clo_track_fds))
+         record_fd_open(tid, res, resolve_fname(res));
 }
 
 PRE(fstat)
@@ -3003,6 +3331,9 @@
    if (!fd_allowed(res, "open", tid)) {
       VG_(close)(res);
       res = -VKI_EMFILE;
+   } else {
+      if(VG_(clo_track_fds))
+         record_fd_open(tid, res, VG_(strdup)((Char*)arg1));
    }
    MAYBE_PRINTF("%d\n",res);
 }
@@ -3044,6 +3375,9 @@
    if (!fd_allowed(res, "creat", tid)) {
       VG_(close)(res);
       res = -VKI_EMFILE;
+   } else {
+      if(VG_(clo_track_fds))
+         record_fd_open(tid, res, VG_(strdup)((Char*)arg1));
    }
    MAYBE_PRINTF("%d\n",res);
 }
@@ -3065,8 +3399,13 @@
       VG_(close)(p[0]);
       VG_(close)(p[1]);
       res = -VKI_EMFILE;
-   } else 
+   } else {
       VG_TRACK( post_mem_write, arg1, 2*sizeof(int) );
+      if(VG_(clo_track_fds)) {
+         record_fd_open(tid, p[0], NULL);
+         record_fd_open(tid, p[1], NULL);
+      }
+   }
 
    MAYBE_PRINTF("SYSCALL[%d]       pipe --> %d (rd %d, wr %d)\n", 
 		VG_(getpid)(), res,
@@ -3563,12 +3902,19 @@
    case SYS_SOCKETPAIR:
       /* XXX TODO: check return fd against VG_MAX_FD */
       VG_TRACK( post_mem_write, ((UInt*)arg2)[3], 2*sizeof(int) );
+      if(VG_(clo_track_fds)) {
+         record_fd_open(tid, ((UInt*)((UInt*)arg2)[3])[0], NULL);
+         record_fd_open(tid, ((UInt*)((UInt*)arg2)[3])[1], NULL);
+      }
       break;
 
    case SYS_SOCKET:
       if (!fd_allowed(res, "socket", tid)) {
 	 VG_(close)(res);
 	 res = -VKI_EMFILE;
+      } else {
+         if(VG_(clo_track_fds))
+            record_fd_open(tid, res, NULL);
       }
       break;
 
@@ -3593,6 +3939,8 @@
 	 if (addr_p != (Addr)NULL) 
 	    buf_and_len_post_check ( tid, res, addr_p, addrlen_p,
 				     "socketcall.accept(addrlen_out)" );
+         if(VG_(clo_track_fds))
+            record_fd_open(tid, res, NULL);
       }
       break;
    }
@@ -3673,6 +4021,7 @@
       struct msghdr *msg = (struct msghdr *)((UInt *)arg2)[ 1 ];
 
       msghdr_foreachfield( tid, msg, post_mem_write_recvmsg );
+      check_cmsg_for_fds( tid, msg );
 
       break;
    }
@@ -4222,15 +4571,15 @@
    SYSB_(chown32,		False),
    SYSB_(lchown32,		False),
    SYSB_(chown,			False),
-   SYSB_(close,			False),
+   SYSBA(close,			False),
    SYSBA(dup,			False),
    SYSBA(dup2,			False),
-   SYSB_(fcntl,			True),
+   SYSBA(fcntl,			True),
    SYSB_(fchdir,		False),
    SYSB_(fchown32,		False),
    SYSB_(fchown,		False),
    SYSB_(fchmod,		False),
-   SYSB_(fcntl64,		True),
+   SYSBA(fcntl64,		True),
    SYSBA(fstat,			False),
    SYSBA(fork,			False),
    SYSB_(fsync,			True),
diff --git a/include/vg_kerneliface.h b/include/vg_kerneliface.h
index fcfc71d..0be5f2b 100644
--- a/include/vg_kerneliface.h
+++ b/include/vg_kerneliface.h
@@ -330,6 +330,10 @@
 #define VKI_O_DIRECTORY     0200000 /* must be a directory */
 #define VKI_O_NOFOLLOW      0400000 /* don't follow links */
 
+#define VKI_SEEK_SET              0
+#define VKI_SEEK_CUR              1
+#define VKI_SEEK_END              2
+
 /* Copied from linux-2.4.19/include/linux/stat.h */
 
 #define VKI_S_IRWXU 00700
@@ -633,6 +637,41 @@
 #define VKI_CLONE_UNTRACED	0x00800000	/* set if the tracing process can't force VKI_CLONE_PTRACE on this clone */
 #define VKI_CLONE_CHILD_SETTID	0x01000000	/* set the TID in the child */
 
+/* This is the structure passed to the getdents syscall. */
+/*
+ * linux/dirent.h
+ */
+typedef struct vki_dirent {
+	long           d_ino;
+	long           d_off;
+	unsigned short d_reclen;
+	char           d_name[256];
+} vki_dirent;
+
+
+
+/* This is the structure passed to the getrlimit syscall. */
+/*
+ * bits/resource.h
+ */
+typedef struct vki_rlimit {
+	unsigned long rlim_cur;
+	unsigned long rlim_max;
+} vki_rlimit;
+
+#define VKI_RLIMIT_NOFILE 7
+
+/* Socket stuff. */
+/*
+ * sys/socket.h
+ */
+typedef unsigned short vki_sa_family_t;
+struct vki_sockaddr {
+  vki_sa_family_t sa_family;     /* Address family. */
+  char            sa_data[14];   /* Address data. */
+};
+
+
 #endif /* ndef __VG_KERNELIFACE_H */
 
 /*--------------------------------------------------------------------*/
diff --git a/include/vg_skin.h b/include/vg_skin.h
index 0101eaa..ba23074 100644
--- a/include/vg_skin.h
+++ b/include/vg_skin.h
@@ -362,6 +362,9 @@
 /* Looks up VG_(client_envp) */
 extern Char* VG_(getenv) ( Char* name );
 
+/* Get client resource limit*/
+extern Int VG_(getrlimit) ( Int resource, struct vki_rlimit *rlim );
+
 /* Crude stand-in for the glibc system() call. */
 extern Int   VG_(system) ( Char* cmd );
 
@@ -420,6 +423,8 @@
 
 /* ------------------------------------------------------------------ */
 /* unistd.h, fcntl.h, sys/stat.h */
+extern Int  VG_(getdents)( UInt fd, struct vki_dirent *dirp, UInt count );
+extern Int  VG_(readlink)( Char* path, Char* buf, UInt bufsize );
 extern Int  VG_(getpid)  ( void );
 extern Int  VG_(getppid) ( void );
 extern Int  VG_(getpgrp) ( void );
@@ -429,6 +434,7 @@
 extern Int  VG_(open)   ( const Char* pathname, Int flags, Int mode );
 extern Int  VG_(read)   ( Int fd, void* buf, Int count);
 extern Int  VG_(write)  ( Int fd, const void* buf, Int count);
+extern Int  VG_(lseek)  ( Int fd, Long offset, Int whence);
 extern void VG_(close)  ( Int fd );
 
 extern Int  VG_(pipe)   ( Int fd[2] );
@@ -514,6 +520,14 @@
 extern Int VG_(waitpid)	     ( Int pid, Int *status, Int options );
 
 /* ------------------------------------------------------------------ */
+/* socket.h. */
+
+extern Int VG_(getsockname) ( Int sd, struct vki_sockaddr *name, Int *namelen);
+extern Int VG_(getpeername) ( Int sd, struct vki_sockaddr *name, Int *namelen);
+extern Int VG_(getsockopt) ( Int sd, Int level, Int optname, void *optval,
+                             Int *optlen);
+
+/* ------------------------------------------------------------------ */
 /* other, randomly useful functions */
 extern UInt VG_(read_millisecond_timer) ( void );