aboutsummaryrefslogtreecommitdiffstats
path: root/erts/epmd/src/epmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/epmd/src/epmd.c')
-rw-r--r--erts/epmd/src/epmd.c629
1 files changed, 629 insertions, 0 deletions
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;
+}
+