From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001
From: Erlang/OTP <otp@erlang.org>
Date: Fri, 20 Nov 2009 14:54:40 +0000
Subject: The R13B03 release.

---
 erts/epmd/Makefile               |   32 +
 erts/epmd/doc/.gitignore         |    0
 erts/epmd/epmd.mk                |   70 +++
 erts/epmd/src/Makefile           |   22 +
 erts/epmd/src/Makefile.in        |  123 ++++
 erts/epmd/src/epmd.c             |  629 +++++++++++++++++++
 erts/epmd/src/epmd.h             |   37 ++
 erts/epmd/src/epmd_cli.c         |  127 ++++
 erts/epmd/src/epmd_int.h         |  346 +++++++++++
 erts/epmd/src/epmd_srv.c         | 1254 ++++++++++++++++++++++++++++++++++++++
 erts/epmd/test/Makefile          |   80 +++
 erts/epmd/test/epmd.spec         |    1 +
 erts/epmd/test/epmd.spec.vxworks |    2 +
 erts/epmd/test/epmd_SUITE.erl    |  835 +++++++++++++++++++++++++
 14 files changed, 3558 insertions(+)
 create mode 100644 erts/epmd/Makefile
 create mode 100644 erts/epmd/doc/.gitignore
 create mode 100644 erts/epmd/epmd.mk
 create mode 100644 erts/epmd/src/Makefile
 create mode 100644 erts/epmd/src/Makefile.in
 create mode 100644 erts/epmd/src/epmd.c
 create mode 100644 erts/epmd/src/epmd.h
 create mode 100644 erts/epmd/src/epmd_cli.c
 create mode 100644 erts/epmd/src/epmd_int.h
 create mode 100644 erts/epmd/src/epmd_srv.c
 create mode 100644 erts/epmd/test/Makefile
 create mode 100644 erts/epmd/test/epmd.spec
 create mode 100644 erts/epmd/test/epmd.spec.vxworks
 create mode 100644 erts/epmd/test/epmd_SUITE.erl

(limited to 'erts/epmd')

diff --git a/erts/epmd/Makefile b/erts/epmd/Makefile
new file mode 100644
index 0000000000..4c1af393ac
--- /dev/null
+++ b/erts/epmd/Makefile
@@ -0,0 +1,32 @@
+# 
+# %CopyrightBegin%
+# 
+# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# 
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+# 
+# %CopyrightEnd%
+# 
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Common Macros
+# ----------------------------------------------------
+SUB_DIRECTORIES = src 
+
+SPECIAL_TARGETS = 
+
+# ----------------------------------------------------
+# Default Subdir Targets
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_subdir.mk
diff --git a/erts/epmd/doc/.gitignore b/erts/epmd/doc/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erts/epmd/epmd.mk b/erts/epmd/epmd.mk
new file mode 100644
index 0000000000..a73f4bc077
--- /dev/null
+++ b/erts/epmd/epmd.mk
@@ -0,0 +1,70 @@
+# 
+# %CopyrightBegin%
+# 
+# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# 
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+# 
+# %CopyrightEnd%
+# 
+# ------------------------------------------------
+# Server defines
+#
+
+# EPMD port number
+#   4365 - Version 4.2                 TCP
+#   4366 - Version 4.3                 TCP
+#   4368 - Version 4.4.0 - 4.6.2       TCP
+#   4369 - Version 4.6.3 - 4.7.4       TCP/UDP
+EPMD_PORT_NO=4369
+
+
+# ------------------------------------------------
+# Client defines
+#
+
+# Node type:
+#    72 = R3 hidden node
+#    77 = R3 erlang node
+#   104 = R4 hidden node
+#   109 = R4 erlang node
+#   (110 = R6 nodes (explicit flags for differences between nodes))
+#
+# What epmd has been told, differs very much between versions, both
+# 111 and 110 seems to have been used to tell epmd, while 
+# the actual nodetypes has still been 104 and 109. 
+# EPMD does not care about this, why we move back to using
+# the correct tag (an 'n') for all nodes.
+#
+EPMD_NODE_TYPE=110
+
+# Lowest/Highest supported version of the distribution protocol:
+#   0 = R3
+#   1 = R4
+#   2 = R5      ?????
+#   3 = R5C
+#   4 = R6 (development)
+#   5 = R6
+# There was no protocol change in release R5, so we didn't need to raise
+# the version number. But now that R5A is released, it's best to keep it
+# this way.
+# The number was inadvertently raised for R5C, so we increase it again
+# for R6.
+# Distribution version 4 means a) distributed monitor and b) larger references
+# in the distribution format. 
+# In format 5, nodes can explicitly tell each other which of the above
+# mentioned capabilities they can handle.
+# Distribution format 5 contains the new md5 based handshake.
+
+EPMD_DIST_LOW=5
+EPMD_DIST_HIGH=5
+
diff --git a/erts/epmd/src/Makefile b/erts/epmd/src/Makefile
new file mode 100644
index 0000000000..7d586c7438
--- /dev/null
+++ b/erts/epmd/src/Makefile
@@ -0,0 +1,22 @@
+# 
+# %CopyrightBegin%
+# 
+# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# 
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+# 
+# %CopyrightEnd%
+#
+# Invoke with GNU make or clearmake -C gnu.
+#
+
+include $(ERL_TOP)/make/run_make.mk
diff --git a/erts/epmd/src/Makefile.in b/erts/epmd/src/Makefile.in
new file mode 100644
index 0000000000..498756b468
--- /dev/null
+++ b/erts/epmd/src/Makefile.in
@@ -0,0 +1,123 @@
+#
+# %CopyrightBegin%
+# 
+# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# 
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+# 
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+
+
+ifeq ($(TYPE),debug)
+PURIFY     =
+TYPEMARKER = .debug
+TYPE_FLAGS = -DDEBUG @DEBUG_FLAGS@
+else
+ifeq ($(TYPE),purify)
+PURIFY     = purify
+TYPEMARKER =
+ifeq ($(findstring ose,$(TARGET)),ose)
+  TYPE_FLAGS = -DPURIFY
+else
+  TYPE_FLAGS = -O2 -DPURIFY
+endif
+else
+PURIFY     =
+TYPEMARKER =
+ifeq ($(findstring ose,$(TARGET)),ose)
+  TYPE_FLAGS =
+else
+  TYPE_FLAGS = -O2
+endif
+endif
+endif
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+include ../../vsn.mk
+include ../epmd.mk
+
+BINDIR  = $(ERL_TOP)/bin/$(TARGET)
+OBJDIR  = $(ERL_TOP)/erts/obj$(TYPEMARKER)/$(TARGET)
+
+CC      = @CC@
+WFLAGS  = @WFLAGS@
+CFLAGS  = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS)
+LD      = @LD@
+LIBS    = @LIBS@
+LDFLAGS = @LDFLAGS@
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+
+# The targets
+ifeq ($(findstring win32,$(TARGET)),win32)
+EPMD = epmd.exe
+else
+EPMD = epmd
+endif
+
+INSTALL_PROGS = $(BINDIR)/$(EPMD)
+
+#---------------------------------
+# Options
+#---------------------------------
+ 
+EPMD_FLAGS = -DEPMD_PORT_NO=$(EPMD_PORT_NO)
+
+#---------------------------------
+# source and object file information
+#---------------------------------
+
+EPMD_OBJS = $(OBJDIR)/epmd.o       \
+               $(OBJDIR)/epmd_cli.o   \
+               $(OBJDIR)/epmd_srv.o 
+
+#---------------------------------
+# Build targets
+#---------------------------------
+
+
+all: $(BINDIR)/$(EPMD)
+
+docs:
+
+clean:
+	rm -f $(BINDIR)/$(EPMD) 
+	rm -f $(ERL_TOP)/erts/obj/$(TARGET)/epmd.o
+	rm -f $(ERL_TOP)/erts/obj/$(TARGET)/epmd_cli.o
+	rm -f $(ERL_TOP)/erts/obj/$(TARGET)/epmd_srv.o 
+	rm -f *.o
+	rm -f *~ core
+
+#
+# Objects & executables
+#
+
+$(BINDIR)/$(EPMD): $(EPMD_OBJS)
+	$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS)
+
+$(OBJDIR)/%.o: %.c
+	$(CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $<
+
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: all
+	$(INSTALL_DIR)     $(RELEASE_PATH)/erts-$(VSN)/bin
+	$(INSTALL_PROGRAM) $(INSTALL_PROGS) $(RELEASE_PATH)/erts-$(VSN)/bin
+
+
+release_docs_spec:
+
diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
new file mode 100644
index 0000000000..23ac421446
--- /dev/null
+++ b/erts/epmd/src/epmd.c
@@ -0,0 +1,629 @@
+/* -*- c-indent-level: 2; c-continued-statement-offset: 2 -*- */ 
+/*
+ * %CopyrightBegin%
+ * 
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * 
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ * 
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ * 
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+#include "epmd.h"     /* Renamed from 'epmd_r4.h' */
+#include "epmd_int.h"
+
+#ifdef _OSE_
+#  include "ose.h"
+#  include "efs.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+
+/* forward declarations */
+
+static void usage(EpmdVars *);
+static void run_daemon(EpmdVars*);
+static int get_port_no(void);
+#ifdef __WIN32__
+static int has_console(void);
+#endif
+
+#ifdef DONT_USE_MAIN
+
+static int epmd_main(int, char **, int);
+
+/* VxWorks fill 10 stack words with zero when a function is called
+   from the shell. So it is safe to have argv and argc as parameters
+   even if they are not given in the call. */
+
+#define MAX_DEBUG 10
+
+int epmd_dbg(int level,int port) /* Utility to debug epmd... */
+{
+  char* argv[MAX_DEBUG+2];
+  char  ibuff[100];
+  int   argc = 0;
+  
+  argv[argc++] = "epmd";
+  if(level > MAX_DEBUG)
+    level = MAX_DEBUG;
+  for(;level;--level)
+    argv[argc++] = "-d";
+  if(port)
+    {
+      argv[argc++] = "-port";
+      sprintf(ibuff,"%d",port);
+      argv[argc++] = ibuff;
+    }
+  argv[argc] = NULL;
+
+  return epmd(argc,argv);
+
+}
+
+static char *mystrdup(char *s)
+{
+    char *r = malloc(strlen(s)+1);
+    strcpy(r,s);
+    return r;
+}
+
+#ifdef _OSE_
+
+struct args_sig {
+  SIGSELECT sig_no;
+  int argc ;
+  char argv[20][20];
+};
+
+union SIGNAL {
+  SIGSELECT sig_no;
+  struct args_sig args;
+};
+
+/* Start function. It may be called from the start script as well as from
+   the OSE shell directly (using late start hooks). It spawns epmd as an
+   OSE process which calls the epmd main function. */
+int start_ose_epmd(int argc, char **argv) {
+  union SIGNAL *sig;
+  PROCESS epmd_;
+  OSENTRYPOINT ose_epmd;
+  int i;
+
+  if(hunt("epmd", 0, &epmd_, NULL)) {
+    fprintf(stderr, "Warning! EPMD already exists (%u).\n", epmd_);
+    return 0;
+  }
+  else {
+    /* copy start args to signal */
+    sig = alloc(sizeof(struct args_sig), 0);
+    sig->args.argc = argc;
+    for(i=0; i<argc; i++) {
+      strcpy((sig->args.argv)[i], argv[i]);
+    }    
+    /* start epmd and send signal */
+    epmd_ = create_process(OS_BG_PROC, /* processtype */
+			   "epmd",      /* name        */
+			   ose_epmd,    /* entrypoint  */
+			   16383,	/* stacksize   */
+			   20,	        /* priority    */
+			   0,	        /* timeslice   */
+			   0,	        /* block       */
+			   NULL,0,0);   /* not used    */
+    efs_clone(epmd_);
+    start(epmd_);
+    send(&sig, epmd_);	
+#ifdef DEBUG
+    printf("EPMD ID: %li\n", epmd_);
+#endif
+  }
+  return 0;
+}
+
+OS_PROCESS(ose_epmd) {
+  union SIGNAL *sig;
+  static const SIGSELECT rec_any_sig[] = { 0 };
+  int i, argc;
+  char **argv;
+
+  sig = receive((SIGSELECT*)rec_any_sig);
+
+  argc = sig->args.argc;
+  argv = (char **)malloc((argc+1)*sizeof(char *));
+  for(i=0; i<argc; i++) {
+    argv[i] = (char *)malloc(strlen((sig->args.argv)[i])+1);
+    strcpy(argv[i], (sig->args.argv)[i]);
+  }
+  argv[argc] = NULL;
+  free_buf(&sig);
+
+  epmd(argc, argv);
+
+  for(i=0; i<argc; i++) {
+    free(argv[i]);
+  }
+  free(argv);
+}
+
+#else  /* ifdef _OSE_ */
+
+/* VxWorks start function */
+int start_epmd(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
+char *a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;     
+{
+  char*  argarr[] = {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9};
+  int    i;
+  char** argv = malloc(sizeof(char *)*10);
+  int    argvsiz = 10;
+  int    argc = 1;
+  char*  tmp = malloc(100);
+  int    tmpsiz = 100;
+  char*  pplast;
+  char*  token;
+  
+  argv[0] = mystrdup("epmd");
+  argv[1] = NULL;
+  
+  for(i=0;i<10;++i)
+    {
+      if(argarr[i] == NULL || *argarr[i] == '\0')
+	continue;
+      if(strlen(argarr[i]) >= tmpsiz)
+	tmp = realloc(tmp, tmpsiz = (strlen(argarr[i])+1));
+      strcpy(tmp,argarr[i]);
+      for(token = strtok_r(tmp," ",&pplast);
+	  token != NULL;
+	  token = strtok_r(NULL," ",&pplast))
+	{
+	  if(argc >= argvsiz - 1)
+	    argv = realloc(argv,sizeof(char *) * (argvsiz += 10));
+	  argv[argc++] = mystrdup(token);
+	  argv[argc] = NULL;
+	}
+    }
+  free(tmp);
+  return taskSpawn("epmd",100,VX_FP_TASK,20000,epmd_main,
+		   argc,(int) argv,1,
+		   0,0,0,0,0,0,0);
+}
+    
+#endif    /* _OSE_ */
+    
+    
+
+
+int epmd(int argc, char **argv)
+{
+  return epmd_main(argc,argv,0);
+}
+
+static int epmd_main(int argc, char** argv, int free_argv)
+#else
+int main(int argc, char** argv)
+#endif /* DONT_USE_MAIN */
+{
+    EpmdVars g_empd_vars;
+    EpmdVars *g = &g_empd_vars;
+#ifdef __WIN32__
+    WORD wVersionRequested;
+    WSADATA wsaData;
+    int err;
+
+    wVersionRequested = MAKEWORD(1, 1);
+
+    err = WSAStartup(wVersionRequested, &wsaData);
+    if (err != 0)
+        epmd_cleanup_exit(g,1);
+
+    if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion ) != 1) {
+        WSACleanup();
+    	epmd_cleanup_exit(g,1);
+    }
+#endif
+#ifdef DONT_USE_MAIN
+    if(free_argv)
+	g->argv = argv;
+    else 
+	g->argv = NULL;
+#else
+    g->argv = NULL;
+#endif
+
+    g->port  = get_port_no();
+    g->debug = 0;
+
+    g->silent         = 0; 
+    g->is_daemon      = 0;
+    g->packet_timeout = CLOSE_TIMEOUT; /* Default timeout */
+    g->delay_accept   = 0;
+    g->delay_write    = 0;
+    g->progname       = argv[0];
+    g->listenfd       = -1;
+    g->conn           = NULL;
+    g->nodes.reg = g->nodes.unreg = g->nodes.unreg_tail = NULL;
+    g->nodes.unreg_count = 0;
+    g->active_conn    = 0;
+
+    argc--;
+    argv++;
+    while (argc > 0) {
+	if ((strcmp(argv[0], "-debug")==0) ||
+	    (strcmp(argv[0], "-d")==0)) {
+	    g->debug += 1;
+	    argv++; argc--;
+	} else if (strcmp(argv[0], "-packet_timeout") == 0) {
+	    if ((argc == 1) ||
+		((g->packet_timeout = atoi(argv[1])) == 0))
+		usage(g);
+	    argv += 2; argc -= 2;
+	} else if (strcmp(argv[0], "-delay_accept") == 0) {
+	    if ((argc == 1) ||
+		((g->delay_accept = atoi(argv[1])) == 0))
+		usage(g);
+	    argv += 2; argc -= 2;
+	} else if (strcmp(argv[0], "-delay_write") == 0) {
+	    if ((argc == 1) ||
+		((g->delay_write = atoi(argv[1])) == 0))
+		usage(g);
+	    argv += 2; argc -= 2;
+	} else if (strcmp(argv[0], "-daemon") == 0) {
+	    g->is_daemon = 1;
+	    argv++; argc--;
+	} else if (strcmp(argv[0], "-kill") == 0) {
+	    if (argc == 1)
+		kill_epmd(g);
+	    else
+		usage(g);
+	    epmd_cleanup_exit(g,0);
+	} else if (strcmp(argv[0], "-port") == 0) {
+	    if ((argc == 1) ||
+		((g->port = atoi(argv[1])) == 0))
+	      usage(g);
+	    argv += 2; argc -= 2;
+	} else if (strcmp(argv[0], "-names") == 0) {
+	    if (argc == 1)
+		epmd_call(g, EPMD_NAMES_REQ);
+	    else
+		usage(g);
+	    epmd_cleanup_exit(g,0);
+	} else if (strcmp(argv[0], "-started") == 0) {
+	    g->silent = 1;
+	    if (argc == 1)
+		epmd_call(g, EPMD_NAMES_REQ);
+	    else
+		usage(g);
+	    epmd_cleanup_exit(g,0);
+	} else if (strcmp(argv[0], "-dump") == 0) {
+	    if (argc == 1)
+		epmd_call(g, EPMD_DUMP_REQ);
+	    else
+		usage(g);
+	    epmd_cleanup_exit(g,0);
+	}
+	else
+	    usage(g);
+    }
+    dbg_printf(g,0,"epmd running - daemon = %d",g->is_daemon);
+
+#ifndef NO_SYSCONF
+    if ((g->max_conn = sysconf(_SC_OPEN_MAX)) <= 0)
+#endif
+      g->max_conn = MAX_FILES;
+  
+    /*
+     * max_conn must not be greater than FD_SETSIZE.
+     * (at least QNX crashes)
+     *
+     * More correctly, it must be FD_SETSIZE - 1, beacuse the
+     * listen FD is stored outside the connection array.
+     */
+  
+    if (g->max_conn > FD_SETSIZE) {
+      g->max_conn = FD_SETSIZE - 1;
+    }
+
+    if (g->is_daemon)  {
+	run_daemon(g);
+    } else {
+	run(g);
+    }
+    return 0;
+}
+
+#ifndef NO_DAEMON
+static void run_daemon(EpmdVars *g)
+{
+    register int child_pid, fd;
+    
+    dbg_tty_printf(g,2,"fork a daemon");
+
+    /* fork to make sure first child is not a process group leader */
+    if (( child_pid = fork()) < 0)
+      {
+#ifndef NO_SYSLOG
+	syslog(LOG_ERR,"erlang mapper daemon cant fork %m");
+#endif
+	epmd_cleanup_exit(g,1);
+      }
+    else if (child_pid > 0)
+      {
+	dbg_tty_printf(g,2,"daemon child is %d",child_pid);
+	epmd_cleanup_exit(g,0);  /*parent */
+      }
+    
+    if (setsid() < 0)
+      {
+	dbg_perror(g,"epmd: Cant setsid()");
+	epmd_cleanup_exit(g,1);
+      }
+
+    /* ???? */
+
+
+    signal(SIGHUP, SIG_IGN);
+
+    /* We don't want to be session leader so we fork again */
+
+    if ((child_pid = fork()) < 0)
+      {
+#ifndef NO_SYSLOG
+	syslog(LOG_ERR,"erlang mapper daemon cant fork 2'nd time %m");
+#endif
+	epmd_cleanup_exit(g,1);
+      }
+    else if (child_pid > 0)
+      {
+	dbg_tty_printf(g,2,"daemon 2'nd child is %d",child_pid);
+	epmd_cleanup_exit(g,0);  /*parent */
+      }
+
+    /* move cwd to root to make sure we are not on a mounted filesystem  */
+    chdir("/");
+    
+    umask(0);
+
+    for (fd = 0; fd < g->max_conn ; fd++) /* close all files ... */
+        close(fd);
+    /* Syslog on linux will try to write to whatever if we dont
+       inform it of that the log is closed. */
+    closelog();
+
+    /* These chouldn't be needed but for safety... */
+
+    open("/dev/null", O_RDONLY); /* Order is important! */
+    open("/dev/null", O_WRONLY);
+    open("/dev/null", O_WRONLY);
+
+    errno = 0;  /* if set by open */
+
+    run(g);
+}
+
+#endif /* NO_DAEMON */    
+
+#ifdef __WIN32__
+static int has_console(void)
+{
+    HANDLE handle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
+			       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (handle == INVALID_HANDLE_VALUE) {
+	return 0;
+    } else {
+        CloseHandle(handle);
+	return 1;
+    }
+}
+
+static void run_daemon(EpmdVars *g)
+{
+    if (has_console()) {
+	if (spawnvp(_P_DETACH, __argv[0], __argv) == -1) {
+	    fprintf(stderr, "Failed to spawn detached epmd\n");
+	    exit(1);
+	}
+	exit(0);
+    }
+
+    close(0);
+    close(1);
+    close(2);
+
+    /* These chouldn't be needed but for safety... */
+
+    open("nul", O_RDONLY);
+    open("nul", O_WRONLY);
+    open("nul", O_WRONLY);
+
+    run(g);
+}
+#endif
+
+#if (defined(VXWORKS) || defined(_OSE_))
+static void run_daemon(EpmdVars *g)
+{
+    run(g);
+}
+#endif
+
+
+/***************************************************************************
+ *  Misc support routines
+ *
+ */
+
+static void usage(EpmdVars *g)
+{
+    fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon]\n");
+    fprintf(stderr, "            [-d|-debug] [-port No] [-names|-kill]\n\n");
+    fprintf(stderr, "See the Erlang epmd manual page for info about the usage.\n");
+    fprintf(stderr, "The -port and DbgExtra options are\n\n");
+    fprintf(stderr, "    -port No\n");
+    fprintf(stderr, "        Let epmd listen to another port than default %d\n",
+	    EPMD_PORT_NO);
+    fprintf(stderr, "    -d\n");
+    fprintf(stderr, "    -debug\n");
+    fprintf(stderr, "        Enable debugging. This will give a log to\n");
+    fprintf(stderr, "        the standard error stream. It will shorten\n");
+    fprintf(stderr, "        the number of saved used node names to 5.\n\n");
+    fprintf(stderr, "        If you give more than one debug flag you may\n");
+    fprintf(stderr, "        get more debugging information.\n\n");
+    fprintf(stderr, "    -packet_timout Seconds\n");
+    fprintf(stderr, "        Set the number of seconds a connection can be\n");
+    fprintf(stderr, "        inactive before epmd times out and closes the\n");
+    fprintf(stderr, "        connection (default 60).\n\n");
+    fprintf(stderr, "    -delay_accept Seconds\n");
+    fprintf(stderr, "        To simulate a busy server you can insert a\n");
+    fprintf(stderr, "        delay between epmd gets notified about that\n");
+    fprintf(stderr, "        a new connection is requested and when the\n");
+    fprintf(stderr, "        connections gets accepted.\n\n");
+    fprintf(stderr, "    -delay_write Seconds\n");
+    fprintf(stderr, "        Also a simulation of a busy server. Inserts\n");
+    fprintf(stderr, "        a delay before a reply is sent.\n");
+    epmd_cleanup_exit(g,1);
+}
+
+/***************************************************************************
+ *  Error reporting - dbg_printf() & dbg_tty_printf & dbg_perror()
+ *
+ *  The first form will print out on tty or syslog depending on
+ *  if it runs as deamon or not. The second form will never print
+ *  out on syslog.
+ *
+ *  The arguments are
+ *
+ *      g            Epmd variables
+ *      from_level   From what debug level we print. 0 means always.
+ *                   (This argument is missing from dbg_perror() )
+ *      format       Format string
+ *      args...      Arguments to print out according to the format
+ *      
+ */
+
+static void dbg_gen_printf(int onsyslog,int perr,int from_level,
+			   EpmdVars *g,const char *format, va_list args)
+{
+  time_t now;
+  char *timestr;
+  char buf[2048];
+
+  if (g->is_daemon)
+    {
+#ifndef NO_SYSLOG
+      if (onsyslog)
+	{
+	  vsprintf(buf, format, args);
+	  syslog(LOG_ERR,"epmd: %s",buf);
+	}
+#endif
+    }
+  else
+    {
+      int len;
+
+      time(&now);
+      timestr = (char *)ctime(&now);
+      sprintf(buf, "epmd: %.*s: ", (int) strlen(timestr)-1, timestr);
+      len = strlen(buf);
+      vsprintf(buf + len, format, args);
+      if (perr == 1)
+	perror(buf);
+      else
+	fprintf(stderr,"%s\r\n",buf);
+    }
+}
+
+
+void dbg_perror(EpmdVars *g,const char *format,...)
+{
+  va_list args;
+  va_start(args, format);
+  dbg_gen_printf(1,1,0,g,format,args);
+  va_end(args);
+}
+
+
+void dbg_tty_printf(EpmdVars *g,int from_level,const char *format,...)
+{
+  if (g->debug >= from_level) {
+    va_list args;
+    va_start(args, format);
+    dbg_gen_printf(0,0,from_level,g,format,args);
+    va_end(args);
+  }
+}
+
+void dbg_printf(EpmdVars *g,int from_level,const char *format,...)
+{
+  if (g->debug >= from_level) {
+    va_list args;
+    va_start(args, format);
+    dbg_gen_printf(1,0,from_level,g,format,args);
+    va_end(args);
+  }
+}
+
+
+/***************************************************************************
+ *
+ * This function is to clean up all filedescriptors and free up memory on 
+ * VxWorks.
+ * This function exits, there is nothing else to do when all here is run.
+ *
+ */
+
+static void free_all_nodes(EpmdVars *g)
+{
+    Node *tmp;
+    for(tmp=g->nodes.reg; tmp != NULL; tmp = g->nodes.reg){
+	g->nodes.reg = tmp->next;
+	free(tmp);
+    }
+    for(tmp=g->nodes.unreg; tmp != NULL; tmp = g->nodes.unreg){
+	g->nodes.unreg = tmp->next;
+	free(tmp);
+    }
+}
+void epmd_cleanup_exit(EpmdVars *g, int exitval)
+{
+  int i;
+
+  if(g->conn){
+      for (i = 0; i < g->max_conn; i++)
+	  if (g->conn[i].open == EPMD_TRUE)
+	      epmd_conn_close(g,&g->conn[i]);
+      free(g->conn);
+  }
+  if(g->listenfd >= 0)
+      close(g->listenfd);
+  free_all_nodes(g);
+  if(g->argv){
+      for(i=0; g->argv[i] != NULL; ++i)
+	  free(g->argv[i]);
+      free(g->argv);
+  }      
+      
+
+  exit(exitval);
+}
+
+static int get_port_no(void)
+{
+    char* port_str = getenv("ERL_EPMD_PORT");
+    return (port_str != NULL) ? atoi(port_str) : EPMD_PORT_NO;
+}
+
diff --git a/erts/epmd/src/epmd.h b/erts/epmd/src/epmd.h
new file mode 100644
index 0000000000..9e939ee38e
--- /dev/null
+++ b/erts/epmd/src/epmd.h
@@ -0,0 +1,37 @@
+/*
+ * %CopyrightBegin%
+ * 
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * 
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ * 
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ * 
+ * %CopyrightEnd%
+ */
+
+/* The port number is now defined in a makefile */
+
+/* Definitions of message codes */
+
+#define EPMD_ALIVE_REQ 'a'
+#define EPMD_ALIVE_OK_RESP 'Y'
+#define EPMD_PORT_REQ 'p'
+#define EPMD_NAMES_REQ 'n'
+#define EPMD_DUMP_REQ 'd'
+#define EPMD_KILL_REQ 'k'
+#define EPMD_STOP_REQ 's'
+
+/* New epmd messages */
+
+#define EPMD_ALIVE2_REQ 'x' /* 120 */
+#define EPMD_PORT2_REQ 'z' /* 122 */
+#define EPMD_ALIVE2_RESP 'y' /* 121 */
+#define EPMD_PORT2_RESP 'w' /* 119 */
diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c
new file mode 100644
index 0000000000..c12f711bc5
--- /dev/null
+++ b/erts/epmd/src/epmd_cli.c
@@ -0,0 +1,127 @@
+/*
+ * %CopyrightBegin%
+ * 
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * 
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ * 
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ * 
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+#include "epmd.h"     /* Renamed from 'epmd_r4.h' */
+#include "epmd_int.h"
+
+/* forward declarations */
+
+static int conn_to_epmd(EpmdVars*);
+static int read_fill(int,char*,int);
+
+
+void kill_epmd(EpmdVars *g)
+{
+    char buf[5];
+    int fd, rval;
+
+    fd = conn_to_epmd(g);
+    put_int16(1,buf);
+    buf[2] = EPMD_KILL_REQ;
+    if (write(fd, buf, 3) != 3) {
+	printf("epmd: Can't write to epmd\n");
+	epmd_cleanup_exit(g,1);
+    }
+    if ((rval = read_fill(fd,buf,2)) == 2) {
+	printf("Killed\n");
+	epmd_cleanup_exit(g,0);
+    } else if (rval < 0) {
+	printf("epmd: failed to read answer from local epmd\n");
+	epmd_cleanup_exit(g,1);
+    } else { 			/* rval is now 0 or 1 */
+	buf[rval] = '\0';
+	printf("epmd: local epmd responded with <%s>\n", buf);
+	epmd_cleanup_exit(g,1);
+    }
+}
+
+/* what == EPMD_NAMES_REQ || EPMD_DUMP_REQ */
+
+void epmd_call(EpmdVars *g,int what)
+{
+    char buf[OUTBUF_SIZE];
+    int rval,fd,i,j;
+    
+    fd = conn_to_epmd(g);
+    put_int16(1,buf);
+    buf[2] = what;
+    write(fd,buf,3);
+    if (read(fd,(char *)&i,4) != 4) {
+	if (!g->silent)
+	    printf("epmd: no response from local epmd\n");
+	epmd_cleanup_exit(g,1);
+    }
+    j = ntohl(i);
+    if (!g->silent)
+	printf("epmd: up and running on port %d with data:\n", j);
+    while(1) {
+	if ((rval = read(fd,buf,1)) <= 0)  {
+	    close(fd);
+	    epmd_cleanup_exit(g,0);
+	}
+	buf[rval] = '\0';
+	if (!g->silent)
+	    printf("%s",buf);
+    }
+}
+
+
+
+static int conn_to_epmd(EpmdVars *g)
+{
+    struct EPMD_SOCKADDR_IN address;
+    int connect_sock;
+    
+    connect_sock = socket(FAMILY, SOCK_STREAM, 0);
+    if (connect_sock<0)
+	goto error;
+
+    { /* store port number in unsigned short */
+      unsigned short sport = g->port;
+      SET_ADDR_LOOPBACK(address, FAMILY, sport);
+    }
+
+    if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0) 
+	goto error;
+    return connect_sock;
+
+ error:
+    if (!g->silent) {
+	fprintf(stderr, "epmd: Cannot connect to local epmd\n");
+    }
+    epmd_cleanup_exit(g,1);
+    return -1;
+}
+
+/* Fill buffer, return buffer length, 0 for EOF, < 0 for error. */
+static int read_fill(int fd,char *buf,int len)
+{
+    int i;
+    int got = 0;
+    
+    do {
+	if ((i = read(fd, buf+got, len-got)) <= 0) 
+	    return (i);
+	got += i;
+    } while (got < len);
+    return (len);
+}
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
new file mode 100644
index 0000000000..b120b44579
--- /dev/null
+++ b/erts/epmd/src/epmd_int.h
@@ -0,0 +1,346 @@
+/*
+ * %CopyrightBegin%
+ * 
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * 
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ * 
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ * 
+ * %CopyrightEnd%
+ */
+/* 
+ * This file is for internal use within epmd.
+ */
+
+/* This file don't depend on "sys.h" so we have to do some target
+   definitions ourselves */
+
+#ifdef __WIN32__
+#define NO_SYSLOG
+#define NO_SYSCONF
+#define NO_DAEMON
+#endif
+
+#ifdef VXWORKS
+#define NO_SYSLOG
+#define NO_SYSCONF
+#define NO_DAEMON
+#define NO_FCNTL
+#define DONT_USE_MAIN
+#endif
+
+#ifdef _OSE_
+#define NO_SYSLOG
+#define NO_SYSCONF
+#define NO_DAEMON
+#define DONT_USE_MAIN
+#ifndef HAVE_SYS_TIME_H
+#define HAVE_SYS_TIME_H
+#endif
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H
+#endif
+#endif
+
+/* ************************************************************************ */
+/* Standard includes                                                        */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __WIN32__
+#  ifndef WINDOWS_H_INCLUDES_WINSOCK2_H
+#    include <winsock2.h>
+#  endif
+#  include <windows.h>
+#  include <process.h>
+#endif
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#ifdef VXWORKS
+#  include <sys/times.h>
+#  include <time.h>
+#  include <selectLib.h>
+#  include <sysLib.h>
+#  include <sockLib.h>
+#  include <ioLib.h>
+#  include <taskLib.h>
+#  include <rpc/rpc.h>
+#else /* ! VXWORKS */
+#ifndef __WIN32__
+#  ifdef TIME_WITH_SYS_TIME
+#    include <sys/time.h>
+#    include <time.h>
+#  else
+#    ifdef HAVE_SYS_TIME_H
+#       include <sys/time.h>
+#    else
+#       include <time.h>
+#    endif
+#  endif
+#endif
+#endif /* ! VXWORKS */
+
+#if (!defined(__WIN32__) && !defined(_OSE_))
+#  include <netinet/in.h>
+#  include <sys/socket.h>
+#  include <sys/stat.h>
+
+#  ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H
+#    include <rpc/types.h>
+#  endif
+
+#  include <arpa/inet.h>
+#  include <netinet/tcp.h>
+#endif /* ! WIN32 */
+
+#ifndef _OSE_
+#include <ctype.h>
+#include <signal.h>
+#endif
+
+#include <errno.h>
+
+#ifndef NO_SYSLOG
+#  include <syslog.h>
+#endif
+
+#ifdef SYS_SELECT_H
+#  include <sys/select.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
+
+#include <stdarg.h>
+
+#ifdef _OSE_
+#  include "ose.h"
+#  include "inet.h"
+#  include "sys/stat.h"
+#endif
+
+
+/* ************************************************************************ */
+/* Replace some functions by others by making the function name a macro */
+
+#ifdef __WIN32__
+#  define close(s) closesocket((s))
+#  define write(a,b,c) send((a),(b),(c),0)
+#  define read(a,b,c) recv((a),(char *)(b),(c),0)
+#  define sleep(s) Sleep((s) * 1000)
+#  define ioctl(s,r,o) ioctlsocket((s),(r),(o))
+#endif /* WIN32 */
+
+#ifdef VXWORKS
+#define sleep(n) taskDelay((n) * sysClkRateGet())
+#endif /* VXWORKS */
+
+#ifdef _OSE_
+#define sleep(n) delay((n))
+#endif
+
+#ifdef USE_BCOPY
+#  define memcpy(a, b, c) bcopy((b), (a), (c))
+#  define memcmp(a, b, c) bcmp((a), (b), (c))
+#  define memzero(buf, len) bzero((buf), (len))
+#else
+#  define memzero(buf, len) memset((buf), '\0', (len))
+#endif
+
+/* ************************************************************************ */
+/* Try to find replacement values for undefined system parameters           */
+
+#if defined(__WIN32__) && !defined(EADDRINUSE)
+#  define EADDRINUSE WSAEADDRINUSE
+#endif
+
+#ifndef SOMAXCONN
+#  define SOMAXCONN 128
+#endif
+
+/*
+ * How to get max no of file descriptors? We used to use NOFILE from
+ * <sys/param.h>, but that tends to have little relation to reality.
+ * Best is to use sysconf() (POSIX), but we'll just punt if that isn't
+ * available. Start out with a high value because it will also be
+ * used as the number of file descriptors given to select() (it's is
+ * a terrible bug not to have all file descriptors included in the select()).
+ * The value will be adjusted down if FD_SETSIZE is smaller.
+ */
+
+#define MAX_FILES 2048		/* if sysconf() isn't available, or fails */
+
+/* ************************************************************************ */
+/* Macros that let us use IPv6                                              */
+
+#if defined(HAVE_IN6) && defined(AF_INET6) && defined(EPMD6)
+
+#define EPMD_SOCKADDR_IN sockaddr_in6
+#define FAMILY      AF_INET6
+
+#define SET_ADDR_LOOPBACK(addr, af, port) do { \
+    static u_int32_t __addr[4] = IN6ADDR_LOOPBACK_INIT; \
+    memset((char*)&(addr), 0, sizeof(addr)); \
+    (addr).sin6_family = (af); \
+    (addr).sin6_flowinfo = 0; \
+    (addr).sin6_addr.s6_addr32[0] = __addr[0]; \
+    (addr).sin6_addr.s6_addr32[1] = __addr[1]; \
+    (addr).sin6_addr.s6_addr32[2] = __addr[2]; \
+    (addr).sin6_addr.s6_addr32[3] = __addr[3]; \
+    (addr).sin6_port = htons(port); \
+ } while(0)
+
+#define SET_ADDR_ANY(addr, af, port) do { \
+    static u_int32_t __addr[4] = IN6ADDR_ANY_INIT; \
+    memset((char*)&(addr), 0, sizeof(addr)); \
+    (addr).sin6_family = (af); \
+    (addr).sin6_flowinfo = 0; \
+    (addr).sin6_addr.s6_addr32[0] = __addr[0]; \
+    (addr).sin6_addr.s6_addr32[1] = __addr[1]; \
+    (addr).sin6_addr.s6_addr32[2] = __addr[2]; \
+    (addr).sin6_addr.s6_addr32[3] = __addr[3]; \
+    (addr).sin6_port = htons(port); \
+ } while(0)
+
+#else /* Not IP v6 */
+
+#define EPMD_SOCKADDR_IN sockaddr_in
+#define FAMILY      AF_INET
+
+#define SET_ADDR_LOOPBACK(addr, af, port) do { \
+    memset((char*)&(addr), 0, sizeof(addr)); \
+    (addr).sin_family = (af); \
+    (addr).sin_addr.s_addr = htonl(INADDR_LOOPBACK); \
+    (addr).sin_port = htons(port); \
+ } while(0)
+
+#define SET_ADDR_ANY(addr, af, port) do { \
+    memset((char*)&(addr), 0, sizeof(addr)); \
+    (addr).sin_family = (af); \
+    (addr).sin_addr.s_addr = htonl(INADDR_ANY); \
+    (addr).sin_port = htons(port); \
+ } while(0)
+
+#endif /* Not IP v6 */
+
+/* ************************************************************************ */
+/* Our own definitions                                                      */
+
+#define EPMD_FALSE 0
+#define EPMD_TRUE 1
+
+/* If no activity we let select() return every IDLE_TIMEOUT second
+   A file descriptor that are idle for CLOSE_TIMEOUT seconds and
+   isn't a ALIVE socket is probably hanging and we close it */
+
+#define IDLE_TIMEOUT 5
+#define CLOSE_TIMEOUT 60
+
+/* We save the name of nodes that are unregistered. If a new
+   node register the name we want to increment the "creation",
+   a constant 1..3. But we put an limit to this saving to keep
+   the lookup fast and not to leak memory. */
+
+#define MAX_UNREG_COUNT 1000
+#define DEBUG_MAX_UNREG_COUNT 5
+
+/* Maximum length of a node name == atom name */
+#define MAXSYMLEN 255
+
+#define INBUF_SIZE 1024
+#define OUTBUF_SIZE 1024
+
+#define get_int16(s) ((((unsigned char*)  (s))[0] << 8) | \
+                      (((unsigned char*)  (s))[1]))
+
+#define put_int16(i, s) {((unsigned char*)(s))[0] = ((i) >> 8) & 0xff; \
+                        ((unsigned char*)(s))[1] = (i)         & 0xff;}
+
+/* ************************************************************************ */
+
+/* Stuctures used by server */
+
+typedef struct {
+  int fd;			/* File descriptor */
+  unsigned open:1;		/* TRUE if open */
+  unsigned keep:1;		/* Don't close when sent reply */
+  unsigned got;			/* # of bytes we have got */
+  unsigned want;		/* Number of bytes we want */
+  char *buf;			/* The remaining buffer */
+
+  time_t mod_time;		/* Last activity on this socket */
+} Connection;
+
+struct enode {
+  struct enode *next;
+  int fd;			/* The socket in use */
+  unsigned short port;		/* Port number of Erlang node */
+  char symname[MAXSYMLEN+1];	/* Name of the Erlang node */
+  short creation;		/* Started as a random number 1..3 */
+  char nodetype;                /* 77 = normal erlang node 72 = hidden (c-node */
+  char protocol;                /* 0 = tcp/ipv4 */
+  unsigned short highvsn;       /* 0 = OTP-R3 erts-4.6.x, 1 = OTP-R4 erts-4.7.x*/
+  unsigned short lowvsn;
+  char extra[MAXSYMLEN+1];
+};
+
+typedef struct enode Node;
+
+typedef struct {
+  Node *reg;
+  Node *unreg;
+  Node *unreg_tail;
+  int unreg_count;
+} Nodes;
+
+
+/* This is the structure with all variables needed to pass on
+   to all functions. This makes this program reentrant */
+
+typedef struct {
+  int port;
+  int debug;
+  int silent; 
+  int is_daemon;
+  unsigned packet_timeout;
+  unsigned delay_accept;
+  unsigned delay_write;
+  int max_conn;
+  int active_conn;
+  char *progname;
+  Connection *conn;
+  Nodes nodes;
+  fd_set orig_read_mask;
+  int listenfd;
+  char **argv;
+} EpmdVars;
+
+void dbg_printf(EpmdVars*,int,const char*,...);
+void dbg_tty_printf(EpmdVars*,int,const char*,...);
+void dbg_perror(EpmdVars*,const char*,...);
+void kill_epmd(EpmdVars*);
+void epmd_call(EpmdVars*,int);
+void run(EpmdVars*);
+void epmd_cleanup_exit(EpmdVars*, int);
+int epmd_conn_close(EpmdVars*,Connection*);
+
+#ifdef DONT_USE_MAIN
+int  start_epmd(char *,char *,char *,char *,char *,char *,char *,char *,char *,char *);
+int  epmd(int,char **);
+int  epmd_dbg(int,int);
+#endif
+
+
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
new file mode 100644
index 0000000000..b71e27cffd
--- /dev/null
+++ b/erts/epmd/src/epmd_srv.c
@@ -0,0 +1,1254 @@
+/* -*- c-indent-level: 2; c-continued-statement-offset: 2 -*- */ 
+/*
+ * %CopyrightBegin%
+ * 
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * 
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ * 
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ * 
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+#include "epmd.h"     /* Renamed from 'epmd_r4.h' */
+#include "epmd_int.h"
+
+/*
+ *  
+ *  This server is a local name server for Erlang nodes. Erlang nodes can
+ *  ask this server for the listening port of other Erlang nodes on the
+ *  machine EPMD is running on. New distributed nodes that are started
+ *  register their names with this server.
+ *
+ *  To be accessible to all Erlang nodes this server listens to a well
+ *  known port EPMD_PORT_NO (curently port 4369) where requests
+ *  for connections can be sent.
+ *
+ *  To keep track of when registered Erlang nodes are terminated this
+ *  server keeps the socket open where the request for registration was
+ *  made.
+ *
+ *  The protocol is briefly documented in "erl_ext_dist.txt". All requests
+ *  to this server are done with a packet
+ *
+ *      2        n
+ *  +--------+---------+
+ *  | Length | Request |
+ *  +--------+---------+
+ *
+ *  In all but one case there is only one request for each connection made
+ *  to this server so we can safely close the socket after sending the
+ *  reply.  The exception is ALIVE_REQ where we keep the connection
+ *  open without sending any data. When we receive a "close" this is
+ *  an indication that the Erlang node was terminated. The termination
+ *  may have been "normal" or caused by a crash. The operating system
+ *  ensure that the connection is closed either way.
+ *  
+ *  Reading is done non-blocking, i.e. we call a "read" only if we are
+ *  told by the "select" function that there are data to read.
+ *  
+ *  Two databases are used: One node database where the registered names
+ *  of the nodes are stored, and one connection database where the state
+ *  of sockets and the data buffers is stored.
+ *  
+ *  Incomplete packets are thrown away after a timout. The Erlang node
+ *  doing the request is responsible for completing in it in a reasonable time.
+ *
+ *  Note that if the server gets busy it may not have time to
+ *  process all requests for connection. The "accept()" function
+ *  will on most operating systems silently refuse to accept more
+ *  than 5 outstanding requests. It is the client's responsibility
+ *  to retry the request a number of times with random time interval.
+ *  The "-debug" flag will insert a delay so you can test this 
+ *  behaviour.
+ *
+ *  FIXME: In this code we assume that the packets we send on each
+ *  socket is so small that a "write()" never block
+ *
+ *  FIXME: We never restarts a read or write that was terminated
+ *  by an interrupt. Do we need to?
+ *
+ */
+
+/* We use separate data structures for node names and connections
+   so that a request will not use a slot with a name that we
+   want to resuse later incrementing the "creation" */
+
+
+/* forward declarations */
+
+static void do_request(EpmdVars*,int,Connection*,char*,int);
+static int do_accept(EpmdVars*,int);
+static void do_read(EpmdVars*,Connection*);
+static time_t current_time(EpmdVars*);
+
+static Connection *conn_init(EpmdVars*);
+static int conn_open(EpmdVars*,int);
+static int conn_close_fd(EpmdVars*,int);
+
+static void node_init(EpmdVars*);
+static Node *node_reg(EpmdVars*,char*,int,int);
+static Node *node_reg2(EpmdVars*,char*, int, int, unsigned char, unsigned char, int, int, char*);
+static int node_unreg(EpmdVars*,char*);
+static int node_unreg_sock(EpmdVars*,int);
+
+static int reply(EpmdVars*,int,char *,int);
+static void dbg_print_buf(EpmdVars*,char *,int);
+static void print_names(EpmdVars*);
+
+
+void run(EpmdVars *g)
+{
+  int listensock;
+  int i;
+  int opt;
+  struct EPMD_SOCKADDR_IN iserv_addr;
+
+  node_init(g);
+  g->conn = conn_init(g);
+
+  dbg_printf(g,2,"try to initiate listening port %d", g->port);
+  
+  if ((listensock = socket(FAMILY,SOCK_STREAM,0)) < 0) {
+    dbg_perror(g,"error opening stream socket");
+    epmd_cleanup_exit(g,1);
+  }
+  g->listenfd = listensock;
+
+  /*
+   * Initialize number of active file descriptors.
+   * Stdin, stdout, and stderr are still open.
+   * One for the listen socket.
+   */
+  g->active_conn = 3+1;
+  
+  /*
+   * Note that we must not enable the SO_REUSEADDR on Windows,
+   * because addresses will be reused even if they are still in use.
+   */
+  
+#if (!defined(__WIN32__) && !defined(_OSE_))
+  /* We ignore the SIGPIPE signal that is raised when we call write
+     twice on a socket closed by the other end. */
+  signal(SIGPIPE, SIG_IGN);
+
+  opt = 1;			/* Set this option */
+  if (setsockopt(listensock,SOL_SOCKET,SO_REUSEADDR,(char* ) &opt,
+		 sizeof(opt)) <0) {
+    dbg_perror(g,"can't set sockopt");
+    epmd_cleanup_exit(g,1);
+  }
+#endif
+  
+  /* In rare cases select returns because there is someone
+     to accept but the request is withdrawn before the
+     accept function is called. We set the listen socket
+     to be non blocking to prevent us from being hanging
+     in accept() waiting for the next request. */
+#ifdef _OSE_  
+  opt = 1;
+  if (ioctl(listensock, FIONBIO, (char*)&opt) != 0)
+#else
+#if (defined(__WIN32__) || defined(NO_FCNTL))
+  opt = 1;
+  if (ioctl(listensock, FIONBIO, &opt) != 0) /* Gives warning in VxWorks */
+#else
+  opt = fcntl(listensock, F_GETFL, 0);
+  if (fcntl(listensock, F_SETFL, opt | O_NONBLOCK) == -1)
+#endif /* __WIN32__ || VXWORKS */
+#endif /* _OSE_ */
+    dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
+	       listensock);
+
+  { /* store port number in unsigned short */
+    unsigned short sport = g->port;
+    SET_ADDR_ANY(iserv_addr, FAMILY, sport);
+  }
+  
+#ifdef _OSE_
+  {
+    int optlen = sizeof(opt);
+    opt = 1;
+    if(getsockopt(listensock, SOL_SOCKET, SO_REUSEADDR,
+		  (void*)&opt, &optlen) < 0)
+      fprintf(stderr, "\n\nGETSOCKOPT FAILS! %d\n\n", errno);
+    else if(opt == 1)
+      fprintf(stderr, "SO_REUSEADDR is set!\n");
+  }
+#endif
+
+  if(bind(listensock,(struct sockaddr*) &iserv_addr, sizeof(iserv_addr)) < 0 )
+    {
+      if (errno == EADDRINUSE)
+	{
+	  dbg_tty_printf(g,1,"there is already a epmd running at port %d",
+			 g->port);
+	  epmd_cleanup_exit(g,0);
+	}
+      else
+	{
+	  dbg_perror(g,"failed to bind socket");
+	  epmd_cleanup_exit(g,1);
+	}
+    }
+
+  dbg_printf(g,2,"starting");
+
+  listen(listensock, SOMAXCONN);
+
+
+  FD_ZERO(&g->orig_read_mask);
+  FD_SET(listensock,&g->orig_read_mask);
+
+  dbg_tty_printf(g,2,"entering the main select() loop");
+
+ select_again:
+  while(1)
+    {	
+      fd_set read_mask = g->orig_read_mask;
+      struct timeval timeout;
+      int ret;
+
+      /* If we are idle we time out now and then to enable the code
+	 below to close connections that are old and probably
+	 hanging. Make sure that select will return often enough. */
+
+      timeout.tv_sec = (g->packet_timeout < IDLE_TIMEOUT) ? 1 : IDLE_TIMEOUT;
+      timeout.tv_usec = 0;
+
+      if ((ret = select(g->max_conn,&read_mask,(fd_set *)0,(fd_set *)0,&timeout)) < 0)
+	dbg_perror(g,"error in select ");
+      else {
+	time_t now;
+	if (ret == 0) {
+	  FD_ZERO(&read_mask);
+	}
+	if (g->delay_accept) {		/* Test of busy server */
+	  sleep(g->delay_accept);
+	}
+
+	if (FD_ISSET(listensock,&read_mask)) {
+	  if (do_accept(g, listensock) && g->active_conn < g->max_conn) {
+	    /*
+	     * The accept() succeeded, and we have at least one file
+	     * descriptor still free, which means that another accept()
+	     * could succeed. Go do do another select(), in case there
+	     * are more incoming connections waiting to be accepted.
+	     */
+	    goto select_again;
+	  }
+	}
+	  
+	/* Check all open streams marked by select for data or a
+	   close.  We also close all open sockets except ALIVE
+	   with no activity for a long period */
+
+	now = current_time(g);
+	for (i = 0; i < g->max_conn; i++) {
+	  if (g->conn[i].open == EPMD_TRUE) {
+	    if (FD_ISSET(g->conn[i].fd,&read_mask))
+	      do_read(g,&g->conn[i]);
+	    else if ((g->conn[i].keep == EPMD_FALSE) &&
+		     ((g->conn[i].mod_time + g->packet_timeout) < now)) {
+	      dbg_tty_printf(g,1,"closing because timed out on receive");
+	      epmd_conn_close(g,&g->conn[i]);
+	    }
+	  }
+	}
+      }
+    }
+}
+
+/*
+ *  This routine read as much of the packet as possible and
+ *  if completed calls "do_request()" to fullfill the request.
+ *
+ */
+
+static void do_read(EpmdVars *g,Connection *s)
+{
+  int val, pack_size;
+
+  if (s->open == EPMD_FALSE)
+    {
+      dbg_printf(g,0,"read on unknown socket");
+      return;
+    }
+
+  /* Check if we already got the whole packet but we keep the
+     connection alive to find out when a node is terminated. We then
+     want to check for a close */
+
+  if (s->keep == EPMD_TRUE)
+    {
+      val = read(s->fd, s->buf, INBUF_SIZE);
+
+      if (val == 0)
+	{
+	  node_unreg_sock(g,s->fd);
+	  epmd_conn_close(g,s);
+	}
+      else if (val < 0)
+	{
+	    dbg_tty_printf(g,1,"error on ALIVE socket %d (%d; errno=0x%x)",
+			   s->fd, val, errno);
+	  node_unreg_sock(g,s->fd);
+	  epmd_conn_close(g,s);
+	}
+      else
+	{
+	  dbg_tty_printf(g,1,"got more than expected on ALIVE socket %d (%d)",
+		 s->fd,val);
+	  dbg_print_buf(g,s->buf,val);
+
+	  /* FIXME: Shouldn't be needed to close down.... */
+	  node_unreg_sock(g,s->fd);
+	  epmd_conn_close(g,s);
+	}
+      /* FIXME: We always close, probably the right thing to do */
+      return;
+    }
+
+  /* If unknown size we request the whole buffer - what we got - 1
+     We subtract 1 because we will add a "\0" in "do_request()".
+     This is not needed for R3A or higher versions of Erlang,
+     because the '\0' is included in the request,
+     but is kept for backwards compatibility to allow R2D to use
+     this epmd. */
+
+  pack_size = s->want ? s->want : INBUF_SIZE - 1;
+  val = read(s->fd, s->buf + s->got, pack_size - s->got);
+
+  if (val == 0)
+    {
+      /* A close when we haven't got all data */
+      dbg_printf(g,0,"got partial packet only on file descriptor %d (%d)",
+		 s->fd,s->got);
+      epmd_conn_close(g,s);
+      return;
+    }
+
+  if (val < 0)
+    {
+      dbg_perror(g,"error in read");
+      epmd_conn_close(g,s);
+      return;
+    }
+
+  dbg_print_buf(g,s->buf,val);
+
+  s->got += val;
+
+  if ((s->want == 0) && (s->got >= 2))
+    {
+      /* The two byte header that specify the length of the packet
+	 doesn't count the header as part of the packet so we add 2
+	 to "s->want" to make us talk about all bytes we get. */
+
+      s->want = get_int16(s->buf) + 2;
+
+      if ((s->want < 3) || (s->want >= INBUF_SIZE))
+	{
+	  dbg_printf(g,0,"invalid packet size (%d)",s->want - 2);
+	  epmd_conn_close(g,s);
+	  return;
+	}
+
+      if (s->got > s->want)
+	{
+	  dbg_printf(g,0,"got %d bytes in packet, expected %d",
+		     s->got - 2, s->want - 2);
+	  epmd_conn_close(g,s);
+	  return;
+	}
+    }
+  
+  s->mod_time = current_time(g); /* Note activity */
+  
+  if (s->want == s->got)
+    {
+      /* Do action and close up */
+      /* Skip header bytes */
+
+      do_request(g, s->fd, s, s->buf + 2, s->got - 2);
+
+      if (!s->keep)
+	epmd_conn_close(g,s);		/* Normal close */
+    }
+}
+
+static int do_accept(EpmdVars *g,int listensock)
+{
+    int msgsock;
+    struct EPMD_SOCKADDR_IN icli_addr; /* workaround for QNX bug - cannot */
+    int icli_addr_len;            /* handle NULL pointers to accept.  */
+
+    icli_addr_len = sizeof(icli_addr);
+
+    msgsock = accept(listensock,(struct sockaddr*) &icli_addr,
+		     (unsigned int*) &icli_addr_len);
+
+    if (msgsock < 0) {
+        dbg_perror(g,"error in accept");
+	return EPMD_FALSE;
+    }
+
+    return conn_open(g,msgsock);
+}
+
+static void do_request(g, fd, s, buf, bsize)
+     EpmdVars *g;
+     int fd;
+     Connection *s;
+     char *buf;
+     int bsize;
+{
+  char wbuf[OUTBUF_SIZE];	/* Buffer for writing */
+  int i;
+
+  /*
+   * Terminate packet as a C string.  Needed for requests received from Erlang
+   * nodes with lower version than R3A.
+   */
+
+  buf[bsize] = '\0';
+
+  switch (*buf)
+    {
+    case EPMD_ALIVE_REQ:
+      dbg_printf(g,1,"** got ALIVE_REQ");
+
+      /* The packet has the format "axxyyyyyy" where xx is port, given
+	 in network byte order, and yyyyyy is symname, possibly null
+	 terminated. */
+
+      if (buf[bsize - 1] == '\000') /* Skip null termination */
+	bsize--;
+	
+      if (bsize <= 3)
+	{
+	  dbg_printf(g,0,"packet to small for request ALIVE_REQ (%d)", bsize);
+	  return;
+	}
+
+      for (i = 3; i < bsize; i++)
+	if (buf[i] == '\000')
+	  {
+	    dbg_printf(g,0,"node name contains ascii 0 in ALIVE_REQ");
+	    return;
+	  }
+
+      {
+	Node *node;
+	int eport;
+	char *name  = &buf[3];	/* points to node name */
+
+	eport = get_int16(&buf[1]);
+
+	if ((node = node_reg(g, name, fd, eport)) == NULL)
+	  return;
+
+	wbuf[0] = EPMD_ALIVE_OK_RESP;
+	put_int16(node->creation, wbuf+1);
+  
+	if (g->delay_write)		/* Test of busy server */
+	  sleep(g->delay_write);
+
+	if (reply(g, fd, wbuf, 3) != 3)
+	  {
+	    dbg_tty_printf(g,1,"failed to send ALIVE_OK_RESP for \"%s\"",name);
+	    return;
+	  }
+
+	dbg_tty_printf(g,1,"** sent ALIVE_OK_RESP for \"%s\"",name);
+	s->keep = EPMD_TRUE;		/* Don't close on inactivity */
+      }
+      break;
+
+    case EPMD_PORT_REQ:
+      dbg_printf(g,1,"** got PORT_REQ");
+
+      if (buf[bsize - 1] == '\000') /* Skip null termination */
+	bsize--;
+	
+      if (bsize <= 1)
+	{
+	  dbg_printf(g,0,"packet to small for request PORT_REQ (%d)", bsize);
+	  return;
+	}
+
+      for (i = 1; i < bsize; i++)
+	if (buf[i] == '\000')
+	  {
+	    dbg_printf(g,0,"node name contains ascii 0 in PORT_REQ");
+	    return;
+	  }
+
+      {
+	char *name = &buf[1]; /* Points to node name */
+	Node *node;
+
+	for (node = g->nodes.reg; node; node = node->next)
+	  {
+	    if (strcmp(node->symname, name) == 0)
+	      {
+		put_int16(node->port,wbuf);
+		if (reply(g, fd, wbuf, 2) != 2)
+		  {
+		    dbg_tty_printf(g,1,"failed to send PORT_RESP for %s: %d",
+				   name,node->port);
+		    return;
+		  }
+		dbg_tty_printf(g,1,"** sent PORT_RESP for %s: %d",
+			       name,node->port);
+		return;
+	      }
+	  }
+	dbg_tty_printf(g,1,"Closed on PORT_REQ for %s",name);
+      }
+      /* FIXME: How about an answer if no port? Is a close enough? */
+      break;
+
+    case EPMD_ALIVE2_REQ:
+      dbg_printf(g,1,"** got ALIVE2_REQ");
+
+      /* The packet has the format "axxyyyyyy" where xx is port, given
+	 in network byte order, and yyyyyy is symname, possibly null
+	 terminated. */
+
+      if (bsize <= 13)
+	{
+	  dbg_printf(g,0,"packet to small for request ALIVE2_REQ (%d)",bsize);
+	  return;
+	}
+
+      {
+	Node *node;
+	int eport;
+	unsigned char nodetype;
+	unsigned char protocol;
+	unsigned short highvsn;
+	unsigned short lowvsn;
+	int namelen;
+	int extralen;
+	char *name; 
+	char *extra;
+	eport = get_int16(&buf[1]);
+	nodetype = buf[3];
+	protocol = buf[4];
+	highvsn = get_int16(&buf[5]);
+	lowvsn = get_int16(&buf[7]);
+	namelen = get_int16(&buf[9]);
+	extralen = get_int16(&buf[11+namelen]);
+	for (i = 11 ; i < 11 + namelen; i ++)
+	    if (buf[i] == '\000')  {
+		dbg_printf(g,0,"node name contains ascii 0 in ALIVE2_REQ");
+		return;
+	    }
+	name = &buf[11];
+	name[namelen]='\000';
+	extra = &buf[11+namelen+1];
+	extra[extralen]='\000';
+	wbuf[0] = EPMD_ALIVE2_RESP;
+	if ((node = node_reg2(g, name, fd, eport, nodetype, protocol,
+			      highvsn, lowvsn, extra)) == NULL) {
+	    wbuf[1] = 1; /* error */
+	    put_int16(99, wbuf+2);
+	} else {
+	    wbuf[1] = 0; /* ok */
+	    put_int16(node->creation, wbuf+2);
+	}
+  
+	if (g->delay_write)		/* Test of busy server */
+	  sleep(g->delay_write);
+
+	if (reply(g, fd, wbuf, 4) != 4)
+	  {
+	    dbg_tty_printf(g,1,"** failed to send ALIVE2_RESP for \"%s\"",
+			   name);
+	    return;
+	  }
+
+	dbg_tty_printf(g,1,"** sent ALIVE2_RESP for \"%s\"",name);
+	s->keep = EPMD_TRUE;		/* Don't close on inactivity */
+      }
+      break;
+
+    case EPMD_PORT2_REQ:
+      dbg_printf(g,1,"** got PORT2_REQ");
+
+      if (buf[bsize - 1] == '\000') /* Skip null termination */
+	bsize--;
+	
+      if (bsize <= 1)
+	{
+	  dbg_printf(g,0,"packet to small for request PORT2_REQ (%d)", bsize);
+	  return;
+	}
+
+      for (i = 1; i < bsize; i++)
+	if (buf[i] == '\000')
+	  {
+	    dbg_printf(g,0,"node name contains ascii 0 in PORT2_REQ");
+	    return;
+	  }
+
+      {
+	char *name = &buf[1]; /* Points to node name */
+	Node *node;
+	
+	wbuf[0] = EPMD_PORT2_RESP;
+	for (node = g->nodes.reg; node; node = node->next) {
+	    int offset;
+	    if (strcmp(node->symname, name) == 0) {
+		wbuf[1] = 0; /* ok */
+		put_int16(node->port,wbuf+2);
+		wbuf[4] = node->nodetype;
+		wbuf[5] = node->protocol;
+		put_int16(node->highvsn,wbuf+6);
+		put_int16(node->lowvsn,wbuf+8);
+		put_int16(strlen(node->symname),wbuf+10);
+		offset = 12;
+		strcpy(wbuf + offset,node->symname);
+		offset += strlen(node->symname);
+		put_int16(strlen(node->extra),wbuf + offset);
+		offset += 2;
+		strcpy(wbuf + offset,node->extra);
+		offset += (strlen(node->extra)-1);
+		if (reply(g, fd, wbuf, offset) != offset)
+		  {
+		    dbg_tty_printf(g,1,"** failed to send PORT2_RESP (ok) for \"%s\"",name);
+		    return;
+		  }
+		dbg_tty_printf(g,1,"** sent PORT2_RESP (ok) for \"%s\"",name);
+		return;
+	    }
+	}
+	wbuf[1] = 1; /* error */
+	if (reply(g, fd, wbuf, 2) != 2)
+	  {
+	    dbg_tty_printf(g,1,"** failed to send PORT2_RESP (error) for \"%s\"",name);
+	    return;
+	  }
+	dbg_tty_printf(g,1,"** sent PORT2_RESP (error) for \"%s\"",name);
+	return;
+      }
+      break;
+
+    case EPMD_NAMES_REQ:
+      dbg_printf(g,1,"** got NAMES_REQ");
+      {
+	Node *node;
+
+	i = htonl(g->port);
+	memcpy(wbuf,&i,4);
+
+	if (reply(g, fd,wbuf,4) != 4)
+	  {
+	    dbg_tty_printf(g,1,"failed to send NAMES_RESP");
+	    return;
+	  }
+
+	for (node = g->nodes.reg; node; node = node->next)
+	  {
+	    int len;
+
+	    /* CAREFUL!!! These are parsed by "erl_epmd.erl" so a slight
+	       change in syntax will break < OTP R3A */
+
+	    sprintf(wbuf,"name %s at port %d\n",node->symname, node->port);
+	    len = strlen(wbuf);
+	    if (reply(g, fd, wbuf, len) != len)
+	      {
+		dbg_tty_printf(g,1,"failed to send NAMES_RESP");
+		return;
+	      }
+	  }
+      }
+      dbg_tty_printf(g,1,"** sent NAMES_RESP");
+      break;
+
+    case EPMD_DUMP_REQ:
+      dbg_printf(g,1,"** got DUMP_REQ");
+      {
+	Node *node;
+
+	i = htonl(g->port);
+	memcpy(wbuf,&i,4);
+	if (reply(g, fd,wbuf,4) != 4)
+	  {
+	    dbg_tty_printf(g,1,"failed to send DUMP_RESP");
+	    return;
+	  }
+
+	for (node = g->nodes.reg; node; node = node->next)
+	  {
+	    int len;
+
+	    /* CAREFUL!!! These are parsed by "erl_epmd.erl" so a slight
+	       change in syntax will break < OTP R3A */
+
+	    sprintf(wbuf,"active name     <%s> at port %d, fd = %d\n",
+		    node->symname, node->port, node->fd);
+	    len = strlen(wbuf) + 1;
+	    if (reply(g, fd,wbuf,len) != len)
+	      {
+		dbg_tty_printf(g,1,"failed to send DUMP_RESP");
+		return;
+	      }
+	  }
+
+	for (node = g->nodes.unreg; node; node = node->next)
+	  {
+	    int len;
+
+	    /* CAREFUL!!! These are parsed by "erl_epmd.erl" so a slight
+	       change in syntax will break < OTP R3A */
+
+	    sprintf(wbuf,"old/unused name <%s>, port = %d, fd = %d \n",
+		    node->symname,node->port, node->fd);
+	    len = strlen(wbuf) + 1;
+	    if (reply(g, fd,wbuf,len) != len)
+	      {
+		dbg_tty_printf(g,1,"failed to send DUMP_RESP");
+		return;
+	      }
+	  }
+      }
+      dbg_tty_printf(g,1,"** sent DUMP_RESP");
+      break;
+
+    case EPMD_KILL_REQ:
+      dbg_printf(g,1,"** got KILL_REQ");
+      if (reply(g, fd,"OK",2) != 2)
+	dbg_printf(g,0,"failed to send reply to KILL_REQ");
+      dbg_tty_printf(g,1,"epmd killed");
+      conn_close_fd(g,fd);	/* We never return to caller so close here */
+      dbg_printf(g,0,"got KILL_REQ - terminates normal");
+      epmd_cleanup_exit(g,0);			/* Normal exit */
+
+    case EPMD_STOP_REQ:
+      dbg_printf(g,1,"** got STOP_REQ");
+      if (bsize <= 1 )
+	{
+	  dbg_printf(g,0,"packet to small for request STOP_REQ (%d)",bsize);
+	  return;
+	}
+
+      {
+	char *name = &buf[1]; /* Points to node name */
+	int node_fd;
+
+	if ((node_fd = node_unreg(g,name)) < 0)
+	  {
+	    if (reply(g, fd,"NOEXIST",7) != 7)
+	      {
+		dbg_tty_printf(g,1,"failed to send STOP_RESP NOEXIST");
+		return;
+	      }
+	    dbg_tty_printf(g,1,"** sent STOP_RESP NOEXIST");
+	  }
+
+	conn_close_fd(g,node_fd);
+	dbg_tty_printf(g,1,"epmd connection stopped");
+
+	if (reply(g, fd,"STOPPED",7) != 7)
+	  {
+	    dbg_tty_printf(g,1,"failed to send STOP_RESP STOPPED");
+	    return;
+	  }
+	dbg_tty_printf(g,1,"** sent STOP_RESP STOPPED");
+      }
+      break;
+
+    default:
+      dbg_printf(g,0,"got garbage ");
+    }
+}
+
+
+/****************************************************************************
+ *
+ *  Handle database with data for each socket to read
+ *
+ ****************************************************************************/
+
+static Connection *conn_init(EpmdVars *g)
+{
+  int nbytes = g->max_conn * sizeof(Connection);
+  Connection *connections = (Connection *)malloc(nbytes);
+
+  if (connections == NULL)
+    {
+      dbg_printf(g,0,"epmd: Insufficient memory");
+#ifdef DONT_USE_MAIN
+      free(g->argv);
+#endif
+      exit(1);
+    }
+
+  memzero(connections, nbytes);
+
+  return connections;
+}
+
+static int conn_open(EpmdVars *g,int fd)
+{
+  int i;
+  Connection *s;
+
+#ifdef VXWORKS
+  /*
+   * Since file descriptors are global on VxWorks, we might get an fd that
+   * does not fit in the FD_SET.
+   *
+   * Note: This test would be harmless on Unix, but would fail on Windows
+   * because socket are numbered differently and FD_SETs are implemented
+   * differently.
+   */
+  if (fd >= FD_SETSIZE) {
+      dbg_tty_printf(g,0,"file descriptor %d: too high for FD_SETSIZE=%d",
+		     fd,FD_SETSIZE);
+      close(fd);
+      return EPMD_FALSE;
+  }
+#endif
+
+  for (i = 0; i < g->max_conn; i++) {
+    if (g->conn[i].open == EPMD_FALSE) {
+      g->active_conn++;
+      s = &g->conn[i];
+     
+      /* From now on we want to know if there are data to be read */
+      FD_SET(fd, &g->orig_read_mask);
+
+      s->fd   = fd;
+      s->open = EPMD_TRUE;
+      s->keep = EPMD_FALSE;
+      s->want = 0;		/* Currently unknown */
+      s->got  = 0;
+      s->mod_time = current_time(g); /* Note activity */
+
+      s->buf = (char *)malloc(INBUF_SIZE);
+
+      if (s->buf == NULL) {
+	dbg_printf(g,0,"epmd: Insufficient memory");
+	close(fd);
+	return EPMD_FALSE;
+      }
+
+      dbg_tty_printf(g,2,"opening connection on file descriptor %d",fd);
+      return EPMD_TRUE;
+    }
+  }
+
+  dbg_tty_printf(g,0,"failed opening connection on file descriptor %d",fd);
+  close(fd);
+  return EPMD_FALSE;
+}
+
+static int conn_close_fd(EpmdVars *g,int fd)
+{
+  int i;
+
+  for (i = 0; i < g->max_conn; i++)
+    if (g->conn[i].fd == fd)
+      {
+	epmd_conn_close(g,&g->conn[i]);
+	return EPMD_TRUE;
+      }
+  
+  return EPMD_FALSE;
+}
+
+
+int epmd_conn_close(EpmdVars *g,Connection *s)
+{
+  dbg_tty_printf(g,2,"closing connection on file descriptor %d",s->fd);
+
+  FD_CLR(s->fd,&g->orig_read_mask);
+  close(s->fd);			/* Sometimes already closed but close anyway */
+  s->open = EPMD_FALSE;
+  if (s->buf != NULL) {		/* Should never be NULL but test anyway */
+    free(s->buf);
+  }
+  g->active_conn--;
+  return EPMD_TRUE;
+}
+
+/****************************************************************************
+ *
+ *  Handle database with data for each registered node
+ *
+ ****************************************************************************/
+
+
+static void node_init(EpmdVars *g)
+{
+  g->nodes.reg         = NULL;
+  g->nodes.unreg       = NULL;
+  g->nodes.unreg_tail  = NULL;
+  g->nodes.unreg_count = 0;
+}
+
+
+/* We have got a close on a connection and it may be a
+   EPMD_ALIVE_CLOSE_REQ. Note that this call shouild be called
+   *before* calling conn_close() */
+
+static int node_unreg(EpmdVars *g,char *name)
+{
+  Node **prev = &g->nodes.reg;	/* Point to cell pointing to... */
+  Node *node  = g->nodes.reg;	/* Point to first node */
+
+  for (; node; prev = &node->next, node = node->next)
+    if (strcmp(node->symname, name) == 0)
+      {
+	dbg_tty_printf(g,1,"unregistering '%s:%d', port %d",
+		       node->symname, node->creation, node->port);
+
+	*prev = node->next;	/* Link out from "reg" list */
+
+	if (g->nodes.unreg == NULL) /* Link into "unreg" list */
+	  g->nodes.unreg = g->nodes.unreg_tail = node;
+	else
+	  {
+	    g->nodes.unreg_tail->next = node;
+	    g->nodes.unreg_tail = node;
+	  }
+
+	g->nodes.unreg_count++;
+
+	node->next = NULL;	/* Last in list == first in FIFO queue */
+
+	print_names(g);
+
+	return node->fd;
+      }
+
+  dbg_tty_printf(g,1,"trying to unregister node with unknown name %s", name);
+  return -1;
+}
+
+
+static int node_unreg_sock(EpmdVars *g,int fd)
+{
+  Node **prev = &g->nodes.reg;	/* Point to cell pointing to... */
+  Node *node  = g->nodes.reg;	/* Point to first node */
+
+  for (; node; prev = &node->next, node = node->next)
+    if (node->fd == fd)
+      {
+	dbg_tty_printf(g,1,"unregistering '%s:%d', port %d",
+		       node->symname, node->creation, node->port);
+
+	*prev = node->next;	/* Link out from "reg" list */
+
+	if (g->nodes.unreg == NULL) /* Link into "unreg" list */
+	  g->nodes.unreg = g->nodes.unreg_tail = node;
+	else
+	  {
+	    g->nodes.unreg_tail->next = node;
+	    g->nodes.unreg_tail = node;
+	  }
+
+	g->nodes.unreg_count++;
+
+	node->next = NULL;	/* Last in list == first in FIFO queue */
+
+	print_names(g);
+
+	return node->fd;
+      }
+
+  dbg_tty_printf(g,1,
+		 "trying to unregister node with unknown file descriptor %d",
+		 fd);
+  return -1;
+}
+
+/*
+ *  Finding a node slot and a (name,creation) name is a bit tricky.
+ *  We try in order
+ *
+ *  1. If the name was used before and we can reuse that slot but use
+ *     a new "creation" digit in the range 1..3.
+ *
+ *  2. We try to find a new unused slot.
+ *
+ *  3. We try to use an used slot this isn't used any longer.
+ *     FIXME: The criteria for *what* slot to steal should be improved.
+ *     Perhaps use the oldest or something.
+ */
+
+static Node *node_reg(EpmdVars *g,char *name,int fd, int port)
+{
+    return node_reg2(g, name, fd, port, 0, 0, 0, 0, NULL);
+}
+
+static Node *node_reg2(EpmdVars *g,
+		       char* name,
+		       int fd,
+		       int port,
+		       unsigned char nodetype,
+		       unsigned char protocol,
+		       int highvsn,
+		       int lowvsn,
+		       char* extra)
+{
+  Node *prev;			/* Point to previous node or NULL */
+  Node *node;			/* Point to first node */
+
+  /* Can be NULL; means old style */
+  if (extra == NULL)
+     extra = "";
+
+  /* Fail if node name is too long */
+
+  if (strlen(name) > MAXSYMLEN)
+    {
+      dbg_printf(g,0,"node name is too long (%d) %s", strlen(name), name);
+      return NULL;
+    }
+
+  /* Fail if it is already registered */
+
+  for (node = g->nodes.reg; node; node = node->next)
+    if (strcmp(node->symname, name) == 0)
+      {
+	dbg_printf(g,0,"node name already occupied %s", name);
+	return NULL;
+      }
+
+  /* Try to find the name in the used queue so that we
+     can change "creation" number 1..3 */
+
+  prev = NULL;
+
+  for (node = g->nodes.unreg; node; prev = node, node = node->next)
+    if (strcmp(node->symname, name) == 0)
+      {
+	dbg_tty_printf(g,1,"reusing slot with same name '%s'", node->symname);
+
+	if (prev == NULL)	/* First in list matched */
+	  {
+	    if (node->next == NULL) /* Only one element */
+	      g->nodes.unreg = g->nodes.unreg_tail = NULL;
+	    else
+	      g->nodes.unreg = node->next;
+	  }
+	else
+	  {
+	    if (node->next == NULL) /* Last in list */
+	      {
+		g->nodes.unreg_tail = prev; /* Point to new last */
+		prev->next = NULL; /* New last has no next */
+	      }
+	    else
+	      prev->next = node->next; /* Simple link out from list */
+	  }
+
+	g->nodes.unreg_count--;
+
+	/* When reusing we change the "creation" number 1..3 */
+
+	node->creation = node->creation % 3 + 1;
+
+	break;
+      }
+
+  if (node == NULL)
+    {
+      /* A new name. If the "unreg" list is too long we steal the
+	 oldest node structure and use it for the new node, else
+	 we allocate a new node structure */
+
+      if ((g->nodes.unreg_count > MAX_UNREG_COUNT) ||
+	  (g->debug && (g->nodes.unreg_count > DEBUG_MAX_UNREG_COUNT)))
+	{
+	  /* MAX_UNREG_COUNT > 1 so no need to check unreg_tail */
+	  node = g->nodes.unreg;	/* Take first == oldest */
+	  g->nodes.unreg = node->next; /* Link out */
+	  g->nodes.unreg_count--;
+	}
+      else
+	{
+	  if ((node = (Node *)malloc(sizeof(Node))) == NULL)
+	    {
+	      dbg_printf(g,0,"epmd: Insufficient memory");
+	      exit(1);
+	    }
+
+	  node->creation = (current_time(g) % 3) + 1; /* "random" 1-3 */
+	}
+    }
+
+  node->next = g->nodes.reg; /* Link into "reg" queue */
+  g->nodes.reg  = node;
+
+  node->fd       = fd;
+  node->port     = port;
+  node->nodetype = nodetype;
+  node->protocol = protocol;
+  node->highvsn  = highvsn;
+  node->lowvsn   = lowvsn;
+  strcpy(node->extra,extra);
+  strcpy(node->symname,name);
+  FD_SET(fd,&g->orig_read_mask);
+
+  if (highvsn == 0) {
+    dbg_tty_printf(g,1,"registering '%s:%d', port %d",
+		   node->symname, node->creation, node->port);
+  } else {
+    dbg_tty_printf(g,1,"registering '%s:%d', port %d",
+		   node->symname, node->creation, node->port);
+    dbg_tty_printf(g,1,"type %d proto %d highvsn %d lowvsn %d",
+		   nodetype, protocol, highvsn, lowvsn);
+  }      
+
+  print_names(g);
+
+  return node;
+}
+  
+
+static time_t current_time(EpmdVars *g)
+{
+  time_t t = time((time_t *)0);
+  dbg_printf(g,3,"time in seconds: %d",t);
+  return t;
+}
+
+
+static int reply(EpmdVars *g,int fd,char *buf,int len)
+{
+  int val;
+
+  if (len < 0)
+    {
+      dbg_printf(g,0,"Invalid length in write %d",len);
+      return -1;
+    }
+
+  if (g->delay_write)		/* Test of busy server */
+    sleep(g->delay_write);
+
+  val = write(fd,buf,len);
+  if (val < 0)
+    dbg_perror(g,"error in write");
+  else if (val != len)
+    dbg_printf(g,0,"could only send %d bytes out of %d to fd %d",val,len,fd);
+
+  dbg_print_buf(g,buf,len);
+
+  return val;
+}
+      
+
+#define LINEBYTECOUNT 16
+
+static void print_buf_hex(unsigned char *buf,int len,char *prefix)
+{
+  int rows, row;
+
+  rows = len / LINEBYTECOUNT; /* Number of rows */
+  if (len % LINEBYTECOUNT)
+    rows++;			/* If leftovers, add a line for them */
+
+  for (row = 0; row < rows; row++)
+    {
+      int rowstart = row * LINEBYTECOUNT;
+      int rowend   = rowstart + LINEBYTECOUNT;
+      int i;
+
+      fprintf(stderr,"%s%.8x",prefix,rowstart);
+
+      for (i = rowstart; i < rowend; i++)
+	{
+	  if ((i % (LINEBYTECOUNT/2)) == 0)
+	    fprintf(stderr," ");
+
+	  if (i < len)
+	    fprintf(stderr," %.2x",buf[i]);
+	  else
+	    fprintf(stderr,"   ");
+	}
+
+      fprintf(stderr,"  |");
+
+      for (i = rowstart; (i < rowend) && (i < len); i++)
+	{
+	  int c = buf[i];
+
+	  /* Used to be isprint(c) but we want ascii only */
+
+	  if ((c >= 32) && (c <= 126))
+	    fprintf(stderr,"%c",c);
+	  else
+	    fprintf(stderr,".");
+	}
+
+      fprintf(stderr,"|\r\n");
+    }
+}
+
+static void dbg_print_buf(EpmdVars *g,char *buf,int len)
+{
+  int plen;
+
+  if ((g->is_daemon) ||		/* Don't want to write to stderr if daemon */
+      (g->debug < 2))		/* or debug level too low */
+    return;
+
+  dbg_tty_printf(g,1,"got %d bytes",len);
+
+  plen = len > 1024 ? 1024 : len; /* Limit the number of chars to print */
+
+  print_buf_hex((unsigned char*)buf,plen,"***** ");
+
+  if (len != plen)
+    fprintf(stderr,"***** ......and more\r\n");
+}
+
+static void print_names(EpmdVars *g)
+{
+  int count = 0;
+  Node *node;
+
+  if ((g->is_daemon) ||		/* Don't want to write to stderr if daemon */
+      (g->debug < 3))		/* or debug level too low */
+    return;
+
+  for (node = g->nodes.reg; node; node = node->next)
+    {
+      fprintf(stderr,"*****     active name     \"%s#%d\" at port %d, fd = %d\r\n",
+	      node->symname, node->creation, node->port, node->fd);
+      count ++;
+    }
+
+  fprintf(stderr, "*****     reg calculated count  : %d\r\n", count);
+
+  count = 0;
+
+  for (node = g->nodes.unreg; node; node = node->next)
+    {
+      fprintf(stderr,"*****     old/unused name \"%s#%d\"\r\n",
+	      node->symname, node->creation);
+      count ++;
+    }
+
+  fprintf(stderr, "*****     unreg counter         : %d\r\n",
+	  g->nodes.unreg_count);
+  fprintf(stderr, "*****     unreg calculated count: %d\r\n", count);
+}
diff --git a/erts/epmd/test/Makefile b/erts/epmd/test/Makefile
new file mode 100644
index 0000000000..c1d62f0f93
--- /dev/null
+++ b/erts/epmd/test/Makefile
@@ -0,0 +1,80 @@
+#
+# %CopyrightBegin%
+# 
+# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+# 
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+# 
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+include ../epmd.mk
+
+EBIN = .
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= epmd_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELEPMDDIR   = $(RELEASE_PATH)/epmd_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include \
+                     -I$(ERL_TOP)/lib/kernel/src/         \
+                     $(EPMD_FLAGS)
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+tests debug opt: $(TARGET_FILES)
+
+clean:
+	rm -f $(TARGET_FILES)
+	rm -f core *~
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ---------------------------------------------------- 
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec:
+
+release_tests_spec: opt
+	$(INSTALL_DIR) $(RELEPMDDIR)
+	$(INSTALL_DATA) epmd.spec epmd.spec.vxworks $(ERL_FILES) \
+		$(TARGET_FILES) $(RELEPMDDIR)
+	chmod -f -R u+w $(RELEPMDDIR)
+
+release_docs_spec:
+
+
+
+
+
diff --git a/erts/epmd/test/epmd.spec b/erts/epmd/test/epmd.spec
new file mode 100644
index 0000000000..0e2496bc72
--- /dev/null
+++ b/erts/epmd/test/epmd.spec
@@ -0,0 +1 @@
+{topcase, {dir, "../epmd_test"}}.
diff --git a/erts/epmd/test/epmd.spec.vxworks b/erts/epmd/test/epmd.spec.vxworks
new file mode 100644
index 0000000000..476308b481
--- /dev/null
+++ b/erts/epmd/test/epmd.spec.vxworks
@@ -0,0 +1,2 @@
+{topcase, {dir, "../epmd_test"}}.
+{skip,{epmd_rx_SUITE,"EPMD RX does simply not work on VxWorks"}}.
diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
new file mode 100644
index 0000000000..513c87a13e
--- /dev/null
+++ b/erts/epmd/test/epmd_SUITE.erl
@@ -0,0 +1,835 @@
+%%
+%% %CopyrightBegin%
+%% 
+%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% 
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%% 
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%% 
+%% %CopyrightEnd%
+%%
+-module(epmd_SUITE).
+-include("test_server.hrl").
+-include_lib("kernel/include/file.hrl").
+
+
+% Timeout for test cases (rather long to work on slow machines)
+-define(SHORT_TEST_TIMEOUT, ?t:seconds(30)).	% Default
+-define(MEDIUM_TEST_TIMEOUT, ?t:minutes(3)).
+-define(LONG_TEST_TIMEOUT, ?t:minutes(10)).
+
+% Delay inserted into code
+-define(SHORT_PAUSE, 100).
+-define(MEDIUM_PAUSE, ?t:seconds(1)).
+-define(LONG_PAUSE, ?t:seconds(5)).
+
+% Test server specific exports
+-export([all/1, init_per_testcase/2, fin_per_testcase/2]).
+
+-export(
+   [
+    register_name/1,
+    register_names_1/1,
+    register_names_2/1,
+    register_duplicate_name/1,
+    get_port_nr/1,
+    slow_get_port_nr/1,
+    unregister_others_name_1/1,
+    unregister_others_name_2/1,
+    register_overflow/1,
+    name_with_null_inside/1,
+    name_null_terminated/1,
+    stupid_names_req/1,
+
+    no_data/1,
+    one_byte/1,
+    two_bytes/1,
+    partial_packet/1,
+    zero_length/1,
+    too_large/1,
+    alive_req_too_small_1/1,
+    alive_req_too_small_2/1,
+    alive_req_too_large/1
+   ]).
+
+
+% Port we use for testing
+-define(PORT,2243).
+-define(EPMDARGS,"-packet_timeout 1").
+
+-define(DUMMY_PORT, 1000).			% Port number to register
+						% not in real use.
+
+% Timeouts etc inside test cases. Time is in milliseconds.
+-define(CONN_RETRY, 4).				% Times to retry connecting
+-define(CONN_SLEEP, 500).
+-define(CONN_TIMEOUT, 100).
+-define(RECV_TIMEOUT, 2000).
+-define(REG_REPEAT_LIM,1000).
+
+% Message codes in epmd protocol
+-define(EPMD_ALIVE_REQ,	$a).
+-define(EPMD_ALIVE_OK_RESP, $Y).
+-define(EPMD_PORT_REQ,	$p).
+-define(EPMD_NAMES_REQ,	$n).
+-define(EPMD_DUMP_REQ,	$d).
+-define(EPMD_KILL_REQ,	$k).
+-define(EPMD_STOP_REQ,	$s).
+
+%%
+%% all/1
+%%
+
+all(suite) ->
+    [
+     register_name,
+     register_names_1,
+     register_names_2,
+     register_duplicate_name,
+     get_port_nr,
+     slow_get_port_nr,
+     unregister_others_name_1,
+     unregister_others_name_2,
+     register_overflow,
+     name_with_null_inside,
+     name_null_terminated,
+     stupid_names_req,
+
+     no_data,
+     one_byte,
+     two_bytes,
+     partial_packet,
+     zero_length,
+     too_large,
+     alive_req_too_small_1,
+     alive_req_too_small_2,
+     alive_req_too_large
+    ].
+
+%%
+%% Run before and after each test case
+%%
+
+init_per_testcase(_Func, Config) ->
+    Dog = test_server:timetrap(?SHORT_TEST_TIMEOUT),
+    cleanup(),
+    [{watchdog, Dog} | Config].
+
+fin_per_testcase(_Func, Config) ->
+    cleanup(),
+    Dog = ?config(watchdog, Config),
+    catch test_server:timetrap_cancel(Dog),	% We may have canceled already
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+register_name(doc) ->
+    ["Register a name"];
+register_name(suite) ->
+    [];
+register_name(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = register_node("foobar"),
+    ?line ok = close(Sock),			% Unregister
+    ok.
+
+register_names_1(doc) ->
+    ["Register and unregister two nodes"];
+register_names_1(suite) ->
+    [];
+register_names_1(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock1} = register_node("foobar"),
+    ?line {ok,Sock2} = register_node("foozap"),
+    ?line ok = close(Sock1),			% Unregister
+    ?line ok = close(Sock2),			% Unregister
+    ok.
+
+register_names_2(doc) ->
+    ["Register and unregister two nodes"];
+register_names_2(suite) ->
+    [];
+register_names_2(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock1} = register_node("foobar"),
+    ?line {ok,Sock2} = register_node("foozap"),
+    ?line ok = close(Sock2),			% Unregister
+    ?line ok = close(Sock1),			% Unregister
+    ok.
+
+register_duplicate_name(doc) ->
+    ["Two nodes with the same name"];
+register_duplicate_name(suite) ->
+    [];
+register_duplicate_name(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = register_node("foobar"),
+    ?line error = register_node("foobar"),
+    ?line ok = close(Sock),			% Unregister
+    ok.
+
+% Internal function to register a node name, no close, i.e. unregister
+
+register_node(Name) ->
+    register_node(Name,?DUMMY_PORT).
+
+register_node(Name, Port) ->
+    case connect() of
+	{ok,Sock} ->
+	    M = [?EPMD_ALIVE_REQ, put16(Port), Name],
+	    case send(Sock, [size16(M), M]) of
+		ok ->
+		    case recv(Sock,3) of
+			{ok, [?EPMD_ALIVE_OK_RESP,_D1,_D0]} ->
+			    {ok,Sock};
+			Other ->
+			    test_server:format("recv on sock ~w: ~p~n",
+					       [Sock,Other]),
+			    error
+		    end;
+		Other ->
+		    test_server:format("send on sock ~w: ~w~n",[Sock,Other]),
+		    error
+	    end;
+	Other ->
+	    test_server:format("Connect on port ~w: ~p~n",[Port,Other]),
+	    error
+    end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+name_with_null_inside(doc) ->
+    ["Register a name with a null char in it"];
+name_with_null_inside(suite) ->
+    [];
+name_with_null_inside(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line error = register_node("foo\000bar"),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+name_null_terminated(doc) ->
+    ["Register a name with terminating null byte"];
+name_null_terminated(suite) ->
+    [];
+name_null_terminated(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = register_node("foobar\000"),
+    ?line error = register_node("foobar"),
+    ?line ok = close(Sock),			% Unregister
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+stupid_names_req(doc) ->
+    ["Read names from epmd in a stupid way"];
+stupid_names_req(suite) ->
+    [];
+stupid_names_req(Config) when list(Config) ->
+    Dog = ?config(watchdog, Config),
+    test_server:timetrap_cancel(Dog),
+    LongDog = test_server:timetrap(?MEDIUM_TEST_TIMEOUT),
+    ?line ok = epmdrun(),
+    ?line [FirstConn | Conn] = register_many(1, ?REG_REPEAT_LIM, "foo"),
+    ?line unregister_many([FirstConn]),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = check_names(Conn),
+    ?line ok = unregister_many(Conn),
+    test_server:timetrap_cancel(LongDog),
+    ok.
+
+check_names(Conn) ->
+    ?line {ok,Sock} = connect_active(),
+    ?line {ok,Reply} = do_get_names(Sock),
+    ?line SortConn  = lists:sort(Conn),
+    ?line SortReply = lists:sort(Reply),
+    ?line ok = check_names_cmp(SortConn, SortReply),
+    ok.
+    
+
+% Compare if the result was the same as was registered
+
+check_names_cmp([], []) ->
+    ok;
+check_names_cmp([{Name,Port,_Sock} | Conn], [{Name,Port} | Reply]) ->
+    check_names_cmp(Conn, Reply).
+
+
+% This code is taken directly from "erl_epmd.erl" in R3A01
+
+-define(int16(X), [(X bsr 8) band 16#ff, X band 16#ff]).
+-define(u32(X1,X2,X3,X4), 
+	(((X1) bsl 24) bor ((X2) bsl 16) bor ((X3) bsl 8) bor X4)).
+
+do_get_names(Socket) ->
+    inet_tcp:send(Socket, [?int16(1),?EPMD_NAMES_REQ]),
+    receive
+	{tcp, Socket, [P0,P1,P2,P3 | T]} ->
+	    EpmdPort = ?u32(P0,P1,P2,P3),
+	    if EpmdPort == ?PORT ->
+		    names_loop(Socket, T, []);
+	       true ->
+		    close(Socket),
+		    {error, address}
+	    end;
+	{tcp_closed, Socket} ->
+	    {ok, []}
+    end.
+
+names_loop(Socket, Acc, Ps) ->
+    receive
+	{tcp, Socket, Bytes} ->
+	    {NAcc, NPs} = scan_names(Acc ++ Bytes, Ps),
+	    names_loop(Socket, NAcc, NPs);
+	{tcp_closed, Socket} ->
+	    {_, NPs} = scan_names(Acc, Ps),	% Really needed?
+	    {ok, NPs}
+    end.
+
+scan_names(Buf, Ps) ->
+    case scan_line(Buf, []) of
+	{Line, NBuf} ->
+	    case parse_line(Line) of
+		{ok, Entry} -> 
+		    scan_names(NBuf, [Entry | Ps]);
+		error ->
+		    scan_names(NBuf, Ps)
+	    end;
+	[] -> {Buf, Ps}
+    end.
+
+scan_line([$\n | Buf], Line) -> {lists:reverse(Line), Buf};
+scan_line([C | Buf], Line) -> scan_line(Buf, [C|Line]);
+scan_line([], _) -> [].
+
+parse_line([$n,$a,$m,$e,$ | Buf0]) ->
+    case parse_name(Buf0, []) of
+	{Name, Buf1}  ->
+	    case Buf1 of
+		[$a,$t,$ ,$p,$o,$r,$t,$ | Buf2] ->
+		    case catch list_to_integer(Buf2) of
+			{'EXIT', _} -> error;
+			Port -> {ok, {Name, Port}}
+		    end;
+		_ -> error
+	    end;
+	error -> error
+    end;
+parse_line(_) -> error.
+
+
+parse_name([$  | Buf], Name) -> {lists:reverse(Name), Buf};
+parse_name([C | Buf], Name) -> parse_name(Buf, [C|Name]);
+parse_name([], _Name) -> error.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_port_nr(doc) ->
+    ["Register a name on a port and ask about port nr"];
+get_port_nr(suite) ->
+    [];
+get_port_nr(Config) when list(Config) ->
+    port_request([?EPMD_PORT_REQ,"foo"]).
+
+slow_get_port_nr(doc) ->
+    ["Register with slow write and ask about port nr"];
+slow_get_port_nr(suite) ->
+    [];
+slow_get_port_nr(Config) when list(Config) ->
+    port_request([?EPMD_PORT_REQ,d,$f,d,$o,d,$o]).
+
+
+% Internal function used above
+
+port_request(M) ->
+    ?line ok = epmdrun(),
+    Port = 1042,
+    ?line {ok,RSock} = register_node("foo", Port),
+    ?line {ok,Sock} = connect(),
+    ?line ok = send(Sock,[size16(M),M]),
+    R = put16(Port),
+    ?line {ok,R} = recv(Sock, length(R)),
+    ?line ok = close(RSock),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+unregister_others_name_1(doc) ->
+    ["Unregister name of other node"];
+unregister_others_name_1(suite) ->
+    [];
+unregister_others_name_1(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,RSock} = register_node("foo"),
+    ?line {ok,Sock} = connect(),
+    M = [?EPMD_STOP_REQ,"foo"],
+    ?line ok = send(Sock,[size16(M),M]),
+    R = "STOPPED",
+    ?line {ok,R} = recv(Sock,length(R)),
+    ?line ok = close(RSock),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+unregister_others_name_2(doc) ->
+    ["Unregister name of other node"];
+unregister_others_name_2(suite) ->
+    [];
+unregister_others_name_2(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    M = [?EPMD_STOP_REQ,"xxx42"],
+    ?line ok = send(Sock,[size16(M),M]),
+    R = "NOEXIST",
+    ?line {ok,R} = recv(Sock,length(R)),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+register_overflow(doc) ->
+    ["Register too many, clean and redo 10 times"];
+register_overflow(suite) ->
+    [];
+register_overflow(Config) when list(Config) ->
+    Dog = ?config(watchdog, Config),
+    test_server:timetrap_cancel(Dog),
+    LongDog = test_server:timetrap(?LONG_TEST_TIMEOUT),
+    ?line ok = epmdrun(),
+    ?line Conn = register_many(1, ?REG_REPEAT_LIM, "foo"),
+    Count = length(Conn),
+    ?line ok = unregister_many(Conn),
+    sleep(?MEDIUM_PAUSE),
+    test_server:format("Limit was ~w names, now reg/unreg all 10 times~n",
+		       [Count]),
+    ?line ok = register_repeat(Count),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = rregister_repeat(Count),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = register_repeat(Count),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = rregister_repeat(Count),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = register_repeat(Count),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = rregister_repeat(Count),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = register_repeat(Count),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = rregister_repeat(Count),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = register_repeat(Count),
+    sleep(?MEDIUM_PAUSE),
+    ?line ok = rregister_repeat(Count),
+    test_server:timetrap_cancel(LongDog),
+    ok.
+
+register_repeat(Count) ->
+    Conn = register_many(1, ?REG_REPEAT_LIM, "foo"),
+    ok = unregister_many(Conn),
+    if
+	length(Conn) == Count ->
+	    ok;
+	true ->
+	    error
+    end.
+
+rregister_repeat(Count) ->
+    Conn = register_many(1, ?REG_REPEAT_LIM, "foo"),
+    ok = unregister_many(lists:reverse(Conn)),
+    if
+	length(Conn) == Count ->
+	    ok;
+	true ->
+	    error
+    end.
+
+% Return count of successful registrations
+
+register_many(I, N, _Prefix) when I > N ->
+    test_server:format("Done with all ~n", []),
+    [];
+register_many(I, N, Prefix) ->
+    Name = gen_name(Prefix, I),
+    Port = ?DUMMY_PORT + I,				% Just make it up
+    case register_node(Name, Port) of
+	{ok,Sock} ->
+	    [{Name,Port,Sock} | register_many(I + 1, N, Prefix)];
+	Any ->
+	    test_server:format("Can't register: ~w of 1..~w ~w~n", 
+			       [Name,N,Any]),
+	    []
+    end.
+
+unregister_many([]) ->
+    ok;
+unregister_many([{Name,_Port,Sock} | Socks]) ->
+    case close(Sock) of
+	ok ->
+	    unregister_many(Socks);
+	Any ->
+	    test_server:format("Can't unregister: ~w reason ~w~n", [Name,Any]),
+	    error
+    end.
+
+gen_name(Str,Int) ->
+    Str ++ integer_to_list(Int).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+no_data(doc) ->
+    ["Open but send no data"];
+no_data(suite) ->
+    [];
+no_data(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    sleep(?LONG_PAUSE),
+    ?line closed = recv(Sock,1),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+one_byte(doc) ->
+    ["Send one byte only"];
+one_byte(suite) ->
+    [];
+one_byte(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    ?line ok = send(Sock,[0]),
+    sleep(?LONG_PAUSE),
+    ?line closed = recv(Sock,1),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+two_bytes(doc) ->
+    ["Send packet size only"];
+two_bytes(suite) ->
+    [];
+two_bytes(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    ?line ok = send(Sock,[put16(3)]),
+    sleep(?LONG_PAUSE),
+    ?line closed = recv(Sock,1),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+partial_packet(doc) ->
+    ["Got only part of a packet"];
+partial_packet(suite) ->
+    [];
+partial_packet(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    ?line ok = send(Sock,[put16(100),"only a few bytes"]),
+    sleep(?LONG_PAUSE),
+    ?line closed = recv(Sock,1),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+zero_length(doc) ->
+    ["Invalid zero packet size"];
+zero_length(suite) ->
+    [];
+zero_length(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    ?line ok = send(Sock,[0,0,0,0,0,0,0,0,0,0]),
+    sleep(?MEDIUM_PAUSE),
+    ?line closed = recv(Sock,1),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+too_large(doc) ->
+    ["Invalid large packet"];
+too_large(suite) ->
+    [];
+too_large(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    Size = 63000,
+    M = lists:duplicate(Size, $z),
+    ?line ok = send(Sock,[put16(Size),M]),
+    sleep(?MEDIUM_PAUSE),
+    ?line closed = recv(Sock,1),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+alive_req_too_small_1(doc) ->
+    ["Try to register but not enough data"];
+alive_req_too_small_1(suite) ->
+    [];
+alive_req_too_small_1(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    M = [?EPMD_ALIVE_REQ, 42],
+    ?line ok = send(Sock, [size16(M), M]),
+    sleep(?MEDIUM_PAUSE),
+    ?line closed = recv(Sock,1),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+alive_req_too_small_2(doc) ->
+    ["Try to register but not enough data"];
+alive_req_too_small_2(suite) ->
+    [];
+alive_req_too_small_2(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    M = [?EPMD_ALIVE_REQ, put16(?DUMMY_PORT)],
+    ?line ok = send(Sock, [size16(M), M]),
+    sleep(?MEDIUM_PAUSE),
+    ?line closed = recv(Sock,1),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+alive_req_too_large(doc) ->
+    ["Try to register but node name too large"];
+alive_req_too_large(suite) ->
+    [];
+alive_req_too_large(Config) when list(Config) ->
+    ?line ok = epmdrun(),
+    ?line {ok,Sock} = connect(),
+    L = [
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+	 ],
+    M = [?EPMD_ALIVE_REQ, put16(?DUMMY_PORT), L],
+    ?line ok = send(Sock, [size16(M), M]),
+    sleep(?MEDIUM_PAUSE),
+    ?line closed = recv(Sock,1),
+    ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Terminate all tests with killing epmd.
+
+cleanup() ->
+    sleep(?MEDIUM_PAUSE),
+    case connect() of
+	{ok,Sock} ->
+	    M = [?EPMD_KILL_REQ],
+	    send(Sock, [size16(M), M]),
+	    recv(Sock,length("OK")),
+	    close(Sock),
+	    sleep(?MEDIUM_PAUSE);
+	_ ->
+	    true
+    end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Normal debug start of epmd
+
+epmdrun() ->
+    case os:find_executable(epmd) of
+	false ->
+	    {error, {could_not_find_epmd_in_path}};
+	Path ->
+	    epmdrun(Path)
+    end.
+
+epmdrun(Epmd) ->
+  %% test_server:format("epmdrun() => Epmd = ~p",[Epmd]),
+  osrun(Epmd ++ " " ?EPMDARGS " -port " ++ integer_to_list(?PORT)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Start an external process
+
+osrun(Cmd) ->
+    _ = open_port({spawn, Cmd}, []),
+    ok.
+    
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Wrappers of TCP functions
+
+% These two functions is the interface for connect.
+% Passive mode is the default
+
+connect() ->
+    connect(?PORT, passive).
+
+connect_active() ->
+    connect(?PORT, active).
+
+
+% Try a few times before giving up
+
+connect(Port, Mode) ->
+    case connect_repeat(?CONN_RETRY, Port, Mode) of
+	{ok,Sock} ->
+	    {ok,Sock};
+	{error,timeout} ->
+	    timeout;
+	{error,Reason} ->
+	    test_server:format("connect: error: ~w~n",[Reason]),
+	    error;
+	Any ->
+	    test_server:format("connect: unknown message: ~w~n",[Any]),
+	    exit(1)
+    end.
+
+
+% Try a few times before giving up. Pause a small time between
+% each try.
+
+connect_repeat(1, Port, Mode) ->
+    connect_mode(Port, Mode);
+connect_repeat(Retry, Port, Mode) ->
+    case connect_mode(Port, Mode) of
+	{ok,Sock} ->
+	    {ok,Sock};
+	{error,Reason} ->
+	    test_server:format("connect: error: ~w~n",[Reason]),
+	    timer:sleep(?CONN_SLEEP),
+	    connect_repeat(Retry - 1, Port, Mode);
+	Any ->
+	    test_server:format("connect: unknown message: ~w~n",[Any]),
+	    exit(1)
+    end.
+
+connect_mode(Port, active) ->
+    gen_tcp:connect("localhost", Port, [{packet, 0}], ?CONN_TIMEOUT);
+connect_mode(Port, passive) ->
+    gen_tcp:connect("localhost", Port, [{packet, 0}, {active, false}],
+		    ?CONN_TIMEOUT).
+
+
+close(Sock) ->
+    case gen_tcp:close(Sock) of
+	{error,_} ->
+	    error;
+	ok ->
+	    ok;
+	Any ->
+	    test_server:format("unknown message: ~w~n",[Any]),
+	    exit(1)
+    end.
+
+recv(Sock, Len) ->
+    recv(Sock, Len, ?RECV_TIMEOUT).
+
+recv(Sock, Len, Timeout) ->
+    case gen_tcp:recv(Sock, Len, Timeout) of
+	{ok,[]} ->				% Should not be the case
+	    recv(Sock, 1, 1);			% any longer
+	{ok,Data} ->
+	    {ok,Data};
+	{error,timeout} ->
+	    timeout;
+	{error,closed} ->
+	    closed;
+	{error,_}=Error ->
+	    Error;
+	Any ->
+	    test_server:format("unknown message: ~w~n",[Any]),
+	    exit(1)
+    end.
+
+%% Send data to socket. The list can be non flat and contain
+%% the atom 'd' or tuple {d,Seconds} where this is delay
+%% put in between the sent characters.
+
+send(Sock, SendSpec) ->
+    case send(SendSpec, [], Sock) of
+	{ok,[]} ->
+	    ok;
+	{ok,RevBytes} ->
+	    send_direct(Sock, lists:reverse(RevBytes));
+	Any ->
+	    Any
+    end.
+
+
+% If an error, return immediately
+% Collect real characters in the first argument to form
+% a string to send. Only perform "actions", like a delay,
+% when this argument is empty.
+
+send([], RevBytes, _Sock) ->
+    {ok,RevBytes};
+send([Byte | Spec], RevBytes, Sock) when integer(Byte) ->
+    send(Spec, [Byte | RevBytes], Sock);
+send([List | Spec], RevBytes, Sock) when list(List) ->
+    case send(List, RevBytes, Sock) of
+	{ok,Left} ->
+	    send(Spec, Left, Sock);
+	Other ->
+	    Other
+    end;
+send([d | Spec], RevBytes, Sock) ->
+    send([{d,1000} | Spec], RevBytes, Sock);
+send([{d,S} | Spec], RevBytes, Sock) ->
+    case send_direct(Sock, lists:reverse(RevBytes)) of
+	ok ->
+	    timer:sleep(S),
+	    send(Spec, [], Sock);
+	Any ->
+	    Any
+    end.
+
+%%%%
+
+send_direct(Sock, Bytes) ->
+    case gen_tcp:send(Sock, Bytes) of
+	ok ->
+	    ok;
+	{error, closed} ->
+	    closed;
+	{error, _Reason} ->
+	    error;
+	Any ->
+	    test_server:format("unknown message: ~w~n",[Any]),
+	    Any
+    end.
+
+sleep(MilliSeconds) ->
+    timer:sleep(MilliSeconds).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+put16(N) ->
+    [N bsr 8, N band 16#ff].
+
+size16(List) ->
+    N = flat_count(List, 0),
+    [N bsr 8, N  band 16#ff].
+
+flat_count([H|T], N) when is_integer(H) ->
+    flat_count(T, N+1);
+flat_count([H|T], N) when is_list(H) ->
+    flat_count(T, flat_count(H, N));
+flat_count([_|T], N) ->
+    flat_count(T, N);
+flat_count([], N) -> N.
+    
-- 
cgit v1.2.3