diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/erl_interface/src/prog | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/erl_interface/src/prog')
-rw-r--r-- | lib/erl_interface/src/prog/ei_fake_prog.c | 303 | ||||
-rw-r--r-- | lib/erl_interface/src/prog/erl_call.c | 906 | ||||
-rw-r--r-- | lib/erl_interface/src/prog/erl_fake_prog.c | 250 | ||||
-rw-r--r-- | lib/erl_interface/src/prog/erl_start.c | 735 | ||||
-rw-r--r-- | lib/erl_interface/src/prog/erl_start.h | 46 |
5 files changed, 2240 insertions, 0 deletions
diff --git a/lib/erl_interface/src/prog/ei_fake_prog.c b/lib/erl_interface/src/prog/ei_fake_prog.c new file mode 100644 index 0000000000..68eb537211 --- /dev/null +++ b/lib/erl_interface/src/prog/ei_fake_prog.c @@ -0,0 +1,303 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2002-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 is a fake program that contains all functions, variables and + * defined symbols mentioned in the manual. We compile this file to see + * that the header files and created library is complete. + * + * You can't run this program, it is for compiling and linking only. + * + ***************************************************************************/ + +/* This is a link and header file test. Including "ei.h" and linking + with libei.a should be enough */ + +/* Use most of + * CFLAGS="-I../include -g -O2 + * -ansi -pedantic + * -Wall + * -Wshadow + * -Wstrict-prototypes + * -Wmissing-prototypes + * -Wmissing-declarations + * -Wnested-externs + * -Winline + * -Werror" + */ + +/* An exception from using eidef.h, use config.h directly */ +#include "config.h" + +#if defined(HAVE_GMP_H) && defined(HAVE_LIBGMP) +#include <gmp.h> +#endif /* HAVE_GMP_H && HAVE_LIBGMP */ + +/* #include <netdb.h> now included by ei.h */ +#include "ei.h" + +#ifdef VXWORKS +int ei_fake_prog_main(void) +#else +int main(void) +#endif +{ + ErlConnect conp; + Erl_IpAddr thisipaddr = (Erl_IpAddr)0; + FILE *fp = (FILE *)0; + char* charp = "foo"; + double *doublep = NULL; + double doublex = 0.0; + ei_cnode xec; + ei_reg *ei_regp = NULL; + ei_term eterm; + ei_x_buff eix; + erlang_big *bigp = NULL; + erlang_fun efun; + erlang_msg *msgp = NULL; + erlang_msg emsg; + erlang_pid *pidp = NULL; + erlang_pid epid; + erlang_port eport; + erlang_ref eref; + erlang_trace etrace; + int *intp = NULL; + int intx = 0; + long *longp = NULL; + long longx = 0; + short creation = 0; + struct ei_reg_stat *ei_reg_statp = NULL; + struct ei_reg_tabstat *ei_reg_tabstatp = NULL; + struct hostent *hostp = NULL; + unsigned char * ucharp = (unsigned char *)"foo"; + unsigned long *ulongp = NULL; + unsigned long ulongx = 0; + void *voidp = NULL; +#ifndef VXWORKS + EI_LONGLONG *longlongp = (EI_LONGLONG*)NULL; + EI_LONGLONG longlongx = 0; + EI_ULONGLONG *ulonglongp = (EI_ULONGLONG*)NULL; + EI_ULONGLONG ulonglongx = 0; +#endif + + intx = erl_errno; + + ei_connect_init(&xec, charp, charp, creation); + ei_connect_xinit (&xec, charp, charp, charp, thisipaddr, charp, creation); + + ei_connect(&xec, charp); + ei_xconnect (&xec, thisipaddr, charp); + + ei_receive(intx, ucharp, intx); + ei_receive_msg(intx, &emsg, &eix); + ei_xreceive_msg(intx, &emsg, &eix); + + ei_send(intx, &epid, charp, intx); + ei_reg_send(&xec, intx, charp, charp, intx); + + ei_rpc(&xec, intx, charp, charp, charp, intx, &eix); + ei_rpc_to(&xec, intx, charp, charp, charp, intx); + ei_rpc_from(&xec, intx, intx, &emsg, &eix); + + ei_publish(&xec, intx); + ei_accept(&xec, intx, &conp); + ei_unpublish(&xec); + + ei_thisnodename(&xec); + ei_thishostname(&xec); + ei_thisalivename(&xec); + + ei_self(&xec); + + ei_gethostbyname(charp); + ei_gethostbyaddr(charp, intx, intx); + ei_gethostbyname_r(charp, hostp, charp, intx, intp); + ei_gethostbyaddr_r(charp, intx, intx, hostp, charp, intx, intp); + + ei_encode_version(charp, intp); + ei_x_encode_version(&eix); + ei_encode_long(charp, intp, longx); + ei_x_encode_long(&eix, longx); + ei_encode_ulong(charp, intp, ulongx); + ei_x_encode_ulong(&eix, ulongx); + ei_encode_double(charp, intp, doublex); + ei_x_encode_double(&eix, doublex); + ei_encode_boolean(charp, intp, intx); + ei_x_encode_boolean(&eix, intx); + ei_encode_char(charp, intp, 'a'); + ei_x_encode_char(&eix, 'a'); + ei_encode_string(charp, intp, charp); + ei_encode_string_len(charp, intp, charp, intx); + ei_x_encode_string(&eix, charp); + ei_x_encode_string_len(&eix, charp, intx); + ei_encode_atom(charp, intp, charp); + ei_encode_atom_len(charp, intp, charp, intx); + ei_x_encode_atom(&eix, charp); + ei_x_encode_atom_len(&eix, charp, intx); + ei_encode_binary(charp, intp, (void *)0, longx); + ei_x_encode_binary(&eix, (void*)0, intx); + ei_encode_pid(charp, intp, &epid); + ei_x_encode_pid(&eix, &epid); + ei_encode_fun(charp, intp, &efun); + ei_x_encode_fun(&eix, &efun); + ei_encode_port(charp, intp, &eport); + ei_x_encode_port(&eix, &eport); + ei_encode_ref(charp, intp, &eref); + ei_x_encode_ref(&eix, &eref); + ei_encode_trace(charp, intp, &etrace); + ei_x_encode_trace(&eix, &etrace); + ei_encode_tuple_header(charp, intp, intx); + ei_x_encode_tuple_header(&eix, longx); + ei_encode_list_header(charp, intp, intx); + ei_x_encode_list_header(&eix, longx); +/* #define ei_encode_empty_list(buf,i) ei_encode_list_header(buf,i,0) */ + ei_x_encode_empty_list(&eix); + + ei_get_type(charp, intp, intp, intp); + ei_get_type_internal(charp, intp, intp, intp); + + ei_decode_version(charp, intp, intp); + ei_decode_long(charp, intp, longp); + ei_decode_ulong(charp, intp, ulongp); + ei_decode_double(charp, intp, doublep); + ei_decode_boolean(charp, intp, intp); + ei_decode_char(charp, intp, charp); + ei_decode_string(charp, intp, charp); + ei_decode_atom(charp, intp, charp); + ei_decode_binary(charp, intp, (void *)0, longp); + ei_decode_fun(charp, intp, &efun); + free_fun(&efun); + ei_decode_pid(charp, intp, &epid); + ei_decode_port(charp, intp, &eport); + ei_decode_ref(charp, intp, &eref); + ei_decode_trace(charp, intp, &etrace); + ei_decode_tuple_header(charp, intp, intp); + ei_decode_list_header(charp, intp, intp); + + ei_decode_ei_term(charp, intp, &eterm); + + ei_print_term(fp, charp, intp); + ei_s_print_term(&charp, charp, intp); + + ei_x_format(&eix, charp); + ei_x_format_wo_ver(&eix, charp); + + ei_x_new(&eix); + ei_x_new_with_version(&eix); + ei_x_free(&eix); + ei_x_append(&eix, &eix); + ei_x_append_buf(&eix, charp, intx); + ei_skip_term(charp, intp); + + ei_reg_open(intx); + ei_reg_resize(ei_regp, intx); + ei_reg_close(ei_regp); + + ei_reg_setival(ei_regp, charp, longx); + ei_reg_setfval(ei_regp, charp, doublex); + ei_reg_setsval(ei_regp, charp, charp); + ei_reg_setpval(ei_regp, charp, voidp, intx); + + ei_reg_setval(ei_regp, charp, intx); + + ei_reg_getival(ei_regp, charp); + ei_reg_getfval(ei_regp, charp); + ei_reg_getsval(ei_regp, charp); + ei_reg_getpval(ei_regp, charp, intp); + + ei_reg_getval(ei_regp, charp, intx); + + ei_reg_markdirty(ei_regp, charp); + + ei_reg_delete(ei_regp, charp); + + ei_reg_stat(ei_regp, charp, ei_reg_statp); + + ei_reg_tabstat(ei_regp, ei_reg_tabstatp); + + ei_reg_dump(intx, ei_regp, charp, intx); + ei_reg_restore(intx, ei_regp, charp); + ei_reg_purge(ei_regp); + +#if defined(HAVE_GMP_H) && defined(HAVE_LIBGMP) + { + mpz_t obj; + ei_decode_bignum(charp, intp, obj); + ei_encode_bignum(charp, intp, obj); + ei_x_encode_bignum(&eix, obj); + } +#endif /* HAVE_GMP_H && HAVE_LIBGMP */ + +#ifndef VXWORKS + + ei_decode_longlong(charp, intp, longlongp); + ei_decode_ulonglong(charp, intp, ulonglongp); + ei_encode_longlong(charp, intp, longlongx); + ei_encode_ulonglong(charp, intp, ulonglongx); + ei_x_encode_longlong(&eix, longlongx); + ei_x_encode_ulonglong(&eix, ulonglongx); + +#endif + +#ifdef USE_EI_UNDOCUMENTED + + ei_decode_intlist(charp, intp, longp, intp); + + ei_receive_encoded(intx, &charp, intp, msgp, intp); + ei_send_encoded(intx, pidp, charp, intx); + ei_send_reg_encoded(intx, pidp, charp, charp, intx); + + ei_decode_big(charp, intp, bigp); + ei_big_comp(bigp, bigp); + ei_big_to_double(bigp, doublep); + ei_small_to_big(intx, bigp); + ei_alloc_big(intx); + ei_free_big(bigp); + +#endif /* USE_EI_UNDOCUMENTED */ + + return + BUFSIZ + + EAGAIN + + EHOSTUNREACH + + EIO + + EI_BIN + + EI_DELET + + EI_DIRTY + + EI_FLT + + EI_FORCE + + EI_INT + + EI_NOPURGE + + EI_STR + + EMSGSIZE + + ENOMEM + + ERL_ERROR + + ERL_EXIT + + ERL_LINK + + ERL_MSG + + ERL_NO_TIMEOUT + + ERL_REG_SEND + + ERL_SEND + + ERL_TICK + + ERL_TIMEOUT + + ERL_UNLINK + + ETIMEDOUT + + MAXATOMLEN; +} diff --git a/lib/erl_interface/src/prog/erl_call.c b/lib/erl_interface/src/prog/erl_call.c new file mode 100644 index 0000000000..f0d638324d --- /dev/null +++ b/lib/erl_interface/src/prog/erl_call.c @@ -0,0 +1,906 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-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% + * + + */ +/* + * Function: Makes it possible to send and receive Erlang + * messages from the (Unix) command line. + * Note: We don't free any memory at all since we only + * live for a short while. + * + */ + +#ifdef __WIN32__ +#include <winsock2.h> +#include <direct.h> +#include <windows.h> +#include <winbase.h> + +#elif VXWORKS + +#include <stdio.h> +#include <string.h> +#include <vxWorks.h> +#include <hostLib.h> +#include <selectLib.h> +#include <ifLib.h> +#include <sockLib.h> +#include <taskLib.h> +#include <inetLib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <time.h> + +#else /* unix */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/time.h> +#include <unistd.h> +#include <sys/param.h> +#include <netdb.h> +#include <sys/times.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#endif + +#include <stdio.h> +#include <stdlib.h> + +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <signal.h> + +#include "ei.h" +#include "ei_resolve.h" +#include "erl_start.h" /* FIXME remove dependency */ + +#ifdef __WIN32__ +static void initWinSock(void); +#endif + +/* + * Some nice global variables + * (I don't think "nice" is the right word actually... -gordon) + */ +/* FIXME problem for threaded ? */ + +struct call_flags { + int startp; + int cookiep; + int modp; + int evalp; + int randomp; + int use_long_name; /* indicates if -name was used, else -sname or -n */ + int debugp; + int verbosep; + int haltp; + char *cookie; + char *node; + char *hidden; + char *apply; + char *script; +}; + +static void usage_arg(const char *progname, const char *switchname); +static void usage_error(const char *progname, const char *switchname); +static void usage(const char *progname); +static int get_module(char **mbuf, char **mname); +static struct hostent* get_hostent(char *host); +static int do_connect(ei_cnode *ec, char *nodename, struct call_flags *flags); +static int read_stdin(char **buf); +static void split_apply_string(char *str, char **mod, + char **fun, char **args); + + +/*************************************************************************** + * + * XXXXX + * + ***************************************************************************/ + +/* FIXME isn't VxWorks to handle arguments differently? */ +/* FIXME check errors from malloc */ + +#if !defined(VXWORKS) +int main(int argc, char *argv[]) +#else +int erl_call(int argc, char **argv) +#endif +{ + int i = 1,fd,creation; + struct hostent *hp; + char host_name[EI_MAXHOSTNAMELEN+1]; + char nodename[MAXNODELEN+1]; + char *p = NULL; + char *ct = NULL; /* temporary used when truncating nodename */ + int modsize = 0; + char *host = NULL; + char *module = NULL; + char *modname = NULL; + struct call_flags flags = {0}; /* Default 0 and NULL in all fields */ + char* progname = argv[0]; + ei_cnode ec; + + /* Get the command line options */ + while (i < argc) { + if (argv[i][0] != '-') { + usage_error(progname, argv[i]); + } + + if (strcmp(argv[i], "-sname") == 0) { /* -sname NAME */ + if (i+1 >= argc) { + usage_arg(progname, "-sname "); + } + + flags.node = (char *) malloc(strlen(argv[i+1]) + 1); + strcpy(flags.node, argv[i+1]); + i++; + flags.use_long_name = 0; + } else if (strcmp(argv[i], "-name") == 0) { /* -name NAME */ + if (i+1 >= argc) { + usage_arg(progname, "-name "); + } + + flags.node = (char *) malloc(strlen(argv[i+1]) + 1); + strcpy(flags.node, argv[i+1]); + i++; + flags.use_long_name = 1; + } else { + if (strlen(argv[i]) != 2) { + usage_error(progname, argv[i]); + } + + switch (argv[i][1]) { + case 's': + flags.startp = 1; + break; + case 'q': + flags.haltp = 1; + break; + case 'v': + flags.verbosep = 1; + break; + case 'd': + flags.debugp = 1; + break; + case 'r': + flags.randomp = 1; + break; + case 'e': + flags.evalp = 1; + break; + case 'm': + flags.modp = 1; + break; + case 'c': + if (i+1 >= argc) { + usage_arg(progname, "-c "); + } + flags.cookiep = 1; + flags.cookie = (char *) malloc(strlen(argv[i+1]) + 1); + strcpy(flags.cookie, argv[i+1]); + i++; + break; + case 'n': + if (i+1 >= argc) { + usage_arg(progname, "-n "); + } + flags.node = (char *) malloc(strlen(argv[i+1]) + 1); + strcpy(flags.node, argv[i+1]); + flags.use_long_name = 1; + i++; + break; + case 'h': + if (i+1 >= argc) { + usage_arg(progname, "-h "); + } + flags.hidden = (char *) malloc(strlen(argv[i+1]) + 1); + strcpy(flags.hidden, argv[i+1]); + i++; + break; + case 'x': + if (i+1 >= argc) { + usage_arg(progname, "-x "); + } + flags.script = (char *) malloc(strlen(argv[i+1]) + 1); + strcpy(flags.script, argv[i+1]); + i++; + break; + case 'a': + if (i+1 >= argc) { + usage_arg(progname, "-a "); + } + flags.apply = (char *) malloc(strlen(argv[i+1]) + 1); + strcpy(flags.apply, argv[i+1]); + i++; + break; + case '?': + usage(progname); + default: + usage_error(progname, argv[i]); + } + } + i++; + + } /* while */ + + + /* + * Can't have them both ! + */ + if (flags.modp && flags.evalp) { + usage(progname); + } + + /* + * Read an Erlang module from stdin. + */ + if (flags.modp) { + modsize = get_module(&module, &modname); + } + + if (flags.verbosep || flags.debugp) { + fprintf(stderr,"erl_call: " + "node = %s\nCookie = %s\n" + "flags = %s %s %s\n" + "module: name = %s , size = %d\n" + "apply = %s\n", + (flags.node ? flags.node : ""), + (flags.cookie ? flags.cookie : ""), + (flags.startp ? "startp" : ""), + (flags.verbosep ? "verbosep" : ""), + (flags.debugp ? "debugp" : ""), + (modname ? modname : ""), modsize, + (flags.apply ? flags.apply : "" )); + } + + /* + * What we, at least, requires ! + */ + if (flags.node == NULL) { + usage(progname); + } + + if (!flags.cookiep) { + flags.cookie = NULL; + } + + /* FIXME decide how many bits etc or leave to connect_xinit? */ + creation = (time(NULL) % 3) + 1; /* "random" */ + + if (flags.hidden == NULL) { + /* As default we are c17@gethostname */ + i = flags.randomp ? (time(NULL) % 997) : 17; + /* FIXME allocates to small !!! */ + flags.hidden = (char *) malloc(3 + 2 ); /* c17 or cXYZ */ +#if defined(VXWORKS) + sprintf(flags.hidden, "c%d", + i < 0 ? (int) taskIdSelf() : i); +#else + sprintf(flags.hidden, "c%d", + i < 0 ? (int) getpid() : i); +#endif + } + { + /* A name for our hidden node was specified */ + char h_hostname[EI_MAXHOSTNAMELEN+1]; + char h_nodename[MAXNODELEN+1]; + char *h_alivename=flags.hidden; + struct in_addr h_ipadr; + char* ct; + +#ifdef __WIN32__ + /* + * FIXME Extremly ugly, but needed to get ei_gethostbyname() below + * to work. + */ + initWinSock(); +#endif + + gethostname(h_hostname, EI_MAXHOSTNAMELEN); + if ((hp = ei_gethostbyname(h_hostname)) == 0) { + fprintf(stderr,"erl_call: can't resolve hostname %s\n", h_hostname); + exit(1); + } + /* If shortnames cut of the name at first '.' */ + if (flags.use_long_name == 0 && (ct = strchr(hp->h_name, '.')) != NULL) { + *ct = '\0'; + } + strcpy(h_hostname, hp->h_name); + memcpy(&h_ipadr.s_addr, *hp->h_addr_list, sizeof(struct in_addr)); + sprintf(h_nodename, "%s@%s", h_alivename, h_hostname); + + if (ei_connect_xinit(&ec, h_hostname, h_alivename, h_nodename, + (Erl_IpAddr)&h_ipadr, flags.cookie, + (short) creation) < 0) { + fprintf(stderr,"erl_call: can't create C node %s; %d\n", + h_nodename, erl_errno); + exit(1); + } + + } + if ((p = strchr((const char *)flags.node, (int) '@')) == 0) { + strcpy(host_name, ei_thishostname(&ec)); + host = host_name; + } else { + *p = 0; + host = p+1; + } + + /* + * Expand name to a real name (may be ip-address) + */ + /* FIXME better error string */ + if ((hp = get_hostent(host)) == 0) { + fprintf(stderr,"erl_call: can't get_hostent(%s)\n", host); + exit(1); + } + /* If shortnames cut of the name at first '.' */ + if (flags.use_long_name == 0 && (ct = strchr(hp->h_name, '.')) != NULL) { + *ct = '\0'; + } + strcpy(host_name, hp->h_name); + sprintf(nodename, "%s@%s", flags.node, host_name); + + /* + * Try to connect. Start an Erlang system if the + * start option is on and no system is running. + */ + if (flags.startp && !flags.haltp) { + fd = do_connect(&ec, nodename, &flags); + } else if ((fd = ei_connect(&ec, nodename)) < 0) { + /* We failed to connect ourself */ + /* FIXME do we really know we failed because of node not up? */ + if (flags.haltp) { + exit(0); + } else { + fprintf(stderr,"erl_call: failed to connect to node %s\n", + nodename); + exit(1); + } + } + + /* If we are connected and the halt switch is set */ + if (fd && flags.haltp) { + int i = 0; + char *p; + ei_x_buff reply; + + ei_encode_empty_list(NULL, &i); + + p = (char *)malloc(i); + i = 0; /* Reset */ + + ei_encode_empty_list(p, &i); + + ei_x_new_with_version(&reply); + + /* FIXME if fails we want to exit != 0 ? */ + ei_rpc(&ec, fd, "erlang", "halt", p, i, &reply); + free(p); + ei_x_free(&reply); + exit(0); + } + + if (flags.verbosep) { + fprintf(stderr,"erl_call: we are now connected to node \"%s\"\n", + nodename); + } + + /* + * Compile the module read from stdin. + */ + if (flags.modp && (modname != NULL)) { + char fname[256]; + + strcpy(fname, modname); + strcat(fname, ".erl"); + + /* + * ei_format("[~s,~w]", fname, erl_mk_binary(module, modsize)); + */ + + { + int i = 0; + char *p; + ei_x_buff reply; + + ei_encode_list_header(NULL, &i, 2); + ei_encode_string(NULL, &i, fname); + ei_encode_binary(NULL, &i, module, modsize); + ei_encode_empty_list(NULL, &i); + + p = (char *)malloc(i); + i = 0; /* Reset */ + + ei_encode_list_header(p, &i, 2); + ei_encode_string(p, &i, fname); + ei_encode_binary(p, &i, module, modsize); + ei_encode_empty_list(p, &i); + + ei_x_new_with_version(&reply); + + if (ei_rpc(&ec, fd, "file", "write_file", p, i, &reply) < 0) { + free(p); + ei_x_free(&reply); + fprintf(stderr,"erl_call: can't write to source file %s\n", + fname); + exit(1); + } + free(p); + ei_x_free(&reply); + } + + /* Compile AND load file on other node */ + + { + int i = 0; + char *p; + ei_x_buff reply; + + ei_encode_list_header(NULL, &i, 2); + ei_encode_atom(NULL, &i, fname); + ei_encode_empty_list(NULL, &i); + ei_encode_empty_list(NULL, &i); + + p = (char *)malloc(i); + i = 0; /* Reset */ + + ei_encode_list_header(p, &i, 2); + ei_encode_atom(p, &i, fname); + ei_encode_empty_list(p, &i); + ei_encode_empty_list(p, &i); + + ei_x_new_with_version(&reply); + + /* erl_format("[~a,[]]", modname) */ + + if (ei_rpc(&ec, fd, "c", "c", p, i, &reply) < 0) { + free(p); + ei_x_free(&reply); + fprintf(stderr,"erl_call: can't compile file %s\n", fname); + } + free(p); + /* FIXME complete this code + FIXME print out error message as term + if (!erl_match(erl_format("{ok,_}"), reply)) { + fprintf(stderr,"erl_call: compiler errors\n"); + } + */ + ei_x_free(&reply); + } + + } + /* + * Eval the Erlang functions read from stdin/ + */ + if (flags.evalp) { + char *evalbuf; + int len; + + len = read_stdin(&evalbuf); + { + int i = 0; + char *p; + ei_x_buff reply; + + ei_encode_list_header(NULL, &i, 1); + ei_encode_binary(NULL, &i, evalbuf, len); + ei_encode_empty_list(NULL, &i); + + p = (char *)malloc(i); + i = 0; /* Reset */ + + ei_encode_list_header(p, &i, 1); + ei_encode_binary(p, &i, evalbuf, len); + ei_encode_empty_list(p, &i); + + ei_x_new_with_version(&reply); + + /* erl_format("[~w]", erl_mk_binary(evalbuf,len))) */ + + if (ei_rpc(&ec, fd, "lib", "eval_str", p, i, &reply) < 0) { + fprintf(stderr,"erl_call: evaluating input failed: %s\n", + evalbuf); + free(p); + free(evalbuf); /* Allocated in read_stdin() */ + ei_x_free(&reply); + exit(1); + } + i = 0; + ei_print_term(stdout,reply.buff,&i); + free(p); + free(evalbuf); /* Allocated in read_stdin() */ + ei_x_free(&reply); + } + } + /* + * Any Erlang call to be made ? + */ + if (flags.apply != NULL) { + char *mod,*fun,*args; + ei_x_buff e, reply; + + split_apply_string(flags.apply, &mod, &fun, &args); + if (flags.verbosep) { + fprintf(stderr,"erl_call: module = %s, function = %s, args = %s\n", + mod, fun, args); + } + + ei_x_new(&e); /* No version to ei_rpc() */ + + if (ei_x_format_wo_ver(&e, args) < 0) { + /* FIXME no error message and why -1 ? */ + exit(-1); + } + + ei_x_new_with_version(&reply); + + if (ei_rpc(&ec, fd, mod, fun, e.buff, e.index, &reply) < 0) { + /* FIXME no error message and why -1 ? */ + ei_x_free(&e); + ei_x_free(&reply); + exit(-1); + } else { + int i = 0; + ei_print_term(stdout,reply.buff,&i); + ei_x_free(&e); + ei_x_free(&reply); + } + } + + return(0); +} + + +/*************************************************************************** + * + * XXXXX + * + ***************************************************************************/ + +/* + * Get host entry (by address or name) + */ +/* FIXME: will fail on names like '2fun4you'. */ +static struct hostent* get_hostent(char *host) +{ + if (isdigit((int)*host)) { + struct in_addr ip_addr; + int b1, b2, b3, b4; + long addr; + + /* FIXME: Use inet_aton() (or inet_pton() and get v6 for free). */ + if (sscanf(host, "%d.%d.%d.%d", &b1, &b2, &b3, &b4) != 4) { + return NULL; + } + addr = inet_addr(host); + ip_addr.s_addr = htonl(addr); + + return ei_gethostbyaddr((char *)&ip_addr,sizeof(struct in_addr), AF_INET); + } + + return ei_gethostbyname(host); +} /* get_hostent */ + + + + +/* + * This function does only return on success. + */ +static int do_connect(ei_cnode *ec, char *nodename, struct call_flags *flags) +{ + int fd; + int start_flags; + int r; + + start_flags = ERL_START_ENODE | + (flags->use_long_name? ERL_START_LONG : 0) | + (flags->verbosep? ERL_START_VERBOSE : 0) | + (flags->debugp? ERL_START_DEBUG : 0); + + if ((fd = ei_connect(ec, nodename)) >= 0) { + /* success */ + if (flags->verbosep) { + fprintf(stderr,"erl_call: now connected to node %s\n", nodename); + } + } else { + char alive[EI_MAXALIVELEN+1]; + char *hostname; + struct hostent *h; + char *cookieargs[3]; + char **args; + + cookieargs[0] = "-setcookie"; + cookieargs[1] = flags->cookie; + cookieargs[2] = NULL; + + args = (flags->cookie) ? cookieargs : NULL; + + if (!(hostname = strrchr(nodename,'@'))) { + return ERL_BADARG; + } + strncpy(alive,nodename,hostname-nodename); + alive[hostname-nodename] = 0x0; + hostname++; + + h = ei_gethostbyname(hostname); + + + if ((r=erl_start_sys(ec,alive,(Erl_IpAddr)(h->h_addr_list[0]), + start_flags,flags->script,args)) < 0) { + fprintf(stderr,"erl_call: unable to start node, error = %d\n", r); + exit(1); + } + + if ((fd=ei_connect(ec, nodename)) >= 0) { + /* success */ + if (flags->verbosep) { + fprintf(stderr,"erl_call: now connected to node \"%s\"\n", + nodename); + } + } else { + /* (failure) */ + switch (fd) { + case ERL_NO_DAEMON: + fprintf(stderr,"erl_call: no epmd running\n"); + exit(1); + case ERL_CONNECT_FAIL: + fprintf(stderr,"erl_call: connect failed\n"); + exit(1); + case ERL_NO_PORT: + fprintf(stderr,"erl_call: node is not running\n"); + exit(1); + case ERL_TIMEOUT: + fprintf(stderr,"erl_call: connect timed out\n"); + exit(1); + default: + fprintf(stderr,"erl_call: error during connect\n"); + exit(1); + } + } + } + + return fd; +} /* do_connect */ + +#define SKIP_SPACE(s) while(isspace((int)*(s))) (s)++ +#define EAT(s) while (!isspace((int)*(s)) && (*(s) != '\0')) (s)++ + +static void split_apply_string(char *str, + char **mod, + char **fun, + char **args) +{ + char *begin=str; + char *start="start"; + char *empty_list="[]"; + int len; + + SKIP_SPACE(str); + if (*str == '\0') { + fprintf(stderr,"erl_call: wrong format of apply string (1)\n"); + exit(1); + } + + EAT(str); + len = str-begin; + *mod = (char *) calloc(len + 1, sizeof(char)); + memcpy(*mod, begin, len); + + SKIP_SPACE(str); + if (*str == '\0') { + *fun = (char *) calloc(strlen(start)+1, sizeof(char)); + strcpy(*fun, start); + *args = (char *) calloc(strlen(empty_list)+1, sizeof(char)); + strcpy(*args, empty_list); + return; + } + begin = str; + EAT(str); + len = str-begin; + *fun = (char *) calloc(len + 1, sizeof(char)); + memcpy(*fun, begin, len); + + SKIP_SPACE(str); + if (*str == '\0') { + *args = (char *) calloc(strlen(empty_list)+1, sizeof(char)); + strcpy(*args, empty_list); + return; + } + + *args = (char *) calloc(strlen(str) + 1, sizeof(char)); + strcpy(*args, str); + + return; + +} /* split_apply_string */ + + +/* + * Read from stdin until EOF is reached. + * Allocate the buffer needed. + */ +static int read_stdin(char **buf) +{ + int bsize = BUFSIZ; + int len = 0; + int i; + char *tmp = (char *) malloc(bsize); + + while (1) { + if ((i = read(0, &tmp[len], bsize-len)) < 0) { + fprintf(stderr,"erl_call: can't read stdin, errno = %d", errno); + exit(1); + } else if (i == 0) { + break; + } else { + len += i; + if ((len+50) > bsize) { + bsize = len * 2; + tmp = (char *) realloc(tmp, bsize); + } else { + continue; + } + } + } /* while */ + *buf = tmp; + return len; + +} /* read_stdin */ + +/* + * Get the module from stdin. + */ +static int get_module(char **mbuf, char **mname) +{ + char *tmp; + int len,i; + + len = read_stdin(mbuf); + /* + * Now, get the module name. + */ + if ((tmp = strstr(*mbuf, "-module(")) != NULL) { + char *start; + tmp += strlen("-module("); + while ((*tmp) == ' ') tmp++; /* eat space */ + start = tmp; + while (1) { + if (isalnum((int)*tmp) || (*tmp == '_')) { + tmp++; + continue; + } else { + break; + } + } /* while */ + i = tmp - start; + *mname = (char *) calloc(i+1, sizeof(char)); + memcpy(*mname, start, i); + } + free(mbuf); /* Allocated in read_stdin() */ + + return len; + +} /* get_module */ + + +/*************************************************************************** + * + * Different error reporting functions that output usage + * + ***************************************************************************/ + +static void usage_noexit(const char *progname) { + fprintf(stderr,"\nUsage: %s [-[demqrsv]] [-c Cookie] [-h HiddenName] \n", progname); + fprintf(stderr," [-x ErlScript] [-a [Mod [Fun [Args]]]]\n"); + fprintf(stderr," (-n Node | -sname Node | -name Node)\n\n"); +#ifdef __WIN32__ + fprintf(stderr," where: -a apply(Mod,Fun,Args) (e.g -a \"erlang length [[a,b,c]]\"\n"); +#else + fprintf(stderr," where: -a apply(Mod,Fun,Args) (e.g -a 'erlang length [[a,b,c]]'\n"); +#endif + fprintf(stderr," -c cookie string; by default read from ~/.erlang.cookie\n"); + fprintf(stderr," -d direct Erlang output to ~/.erl_call.out.<Nodename>\n"); + fprintf(stderr," -e evaluate contents of standard input (e.g echo \"X=1,Y=2,{X,Y}.\"|erl_call -e ...)\n"); + fprintf(stderr," -h specify a name for the erl_call client node\n"); + fprintf(stderr," -m read and compile Erlang module from stdin\n"); + fprintf(stderr," -n name of Erlang node, same as -name\n"); + fprintf(stderr," -name name of Erlang node, expanded to a fully qualified\n"); + fprintf(stderr," -sname name of Erlang node, short form will be used\n"); + fprintf(stderr," -q halt the Erlang node (overrides the -s switch)\n"); + fprintf(stderr," -r use a random name for the erl_call client node\n"); + fprintf(stderr," -s start a new Erlang node if necessary\n"); + fprintf(stderr," -v verbose mode, i.e print some information on stderr\n"); + fprintf(stderr," -x use specified erl start script, default is erl\n"); +} + +static void usage_arg(const char *progname, const char *switchname) { + fprintf(stderr, "Missing argument(s) for \'%s\'.\n", switchname); + usage_noexit(progname); + exit(1); +} + +static void usage_error(const char *progname, const char *switchname) { + fprintf(stderr, "Illegal argument \'%s\'.\n", switchname); + usage_noexit(progname); + exit(1); +} + +static void usage(const char *progname) { + usage_noexit(progname); + exit(0); +} + + +/*************************************************************************** + * + * OS specific functions + * + ***************************************************************************/ + +#ifdef __WIN32__ +/* + * FIXME This should not be here. This is a quick fix to make erl_call + * work at all on Windows NT. + */ +static void initWinSock(void) +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + static int initialized; + + wVersionRequested = MAKEWORD(1, 1); + if (!initialized) { + initialized = 1; + err = WSAStartup(wVersionRequested, &wsaData); + + if (err != 0) { + fprintf(stderr,"erl_call: " + "Can't initialize windows sockets: %d\n", err); + } + + if ( LOBYTE( wsaData.wVersion ) != 1 || + HIBYTE( wsaData.wVersion ) != 1 ) { + fprintf(stderr,"erl_call: This version of " + "windows sockets not supported\n"); + WSACleanup(); + } + } +} +#endif diff --git a/lib/erl_interface/src/prog/erl_fake_prog.c b/lib/erl_interface/src/prog/erl_fake_prog.c new file mode 100644 index 0000000000..a2b49a0ed9 --- /dev/null +++ b/lib/erl_interface/src/prog/erl_fake_prog.c @@ -0,0 +1,250 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2002-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 is a fake program that contains all functions, variables and + * defined symbols mentioned in the manual. We compile this file to see + * that the header files and created library is complete. + * + * You can't run this program, it is for compiling and linking only. + * + ***************************************************************************/ + +/* Use most of + * CFLAGS="-I../include -g -O2 + * -ansi -pedantic + * -Wall + * -Wshadow + * -Wstrict-prototypes + * -Wmissing-prototypes + * -Wmissing-declarations + * -Wnested-externs + * -Winline + * -Werror" + */ + +/* #include <netdb.h> now included by ei.h */ +#include "erl_interface.h" + +#ifdef VXWORKS +int erl_fake_prog_main(void) +#else +int main(void) +#endif +{ + ei_x_buff eix; + int index = 0; + ETERM **etermpp = NULL, *etermp = NULL; + char *charp = NULL; + unsigned char uchar, **ucharpp = NULL, *ucharp = NULL; + void *voidp = NULL; + Erl_Heap *erl_heapp = NULL; + int intx = 0; + int *intp = NULL; + unsigned int uintx, *uintp; + unsigned long *ulongp = NULL; + long longx = 0; + double doublex = 0.0; + short shortx = 42; + FILE *filep = NULL; + Erl_IpAddr erl_ipaddr = NULL; + ErlMessage *erlmessagep = NULL; + ErlConnect *erlconnectp = NULL; + struct hostent *hostp = NULL; + struct in_addr *inaddrp = NULL; + + /* Converion to erl_interface format is in liberl_interface */ + + intx = erl_errno; + + ei_encode_term(charp, &index, voidp); + ei_x_encode_term(&eix, voidp); + ei_decode_term(charp, &index, voidp); + + erl_init(voidp, longx); + erl_connect_init(intx, charp,shortx); + erl_connect_xinit(charp,charp,charp,erl_ipaddr,charp,shortx); + erl_connect(charp); + erl_xconnect(erl_ipaddr,charp); + erl_close_connection(intx); + erl_receive(intx, ucharp, intx); + erl_receive_msg(intx, ucharp, intx, erlmessagep); + erl_xreceive_msg(intx, ucharpp, intp, erlmessagep); + erl_send(intx, etermp, etermp); + erl_reg_send(intx, charp, etermp); + erl_rpc(intx,charp,charp,etermp); + erl_rpc_to(intx,charp,charp,etermp); + erl_rpc_from(intx,intx,erlmessagep); + + erl_publish(intx); + erl_accept(intx,erlconnectp); + + erl_thiscookie(); + erl_thisnodename(); + erl_thishostname(); + erl_thisalivename(); + erl_thiscreation(); + erl_unpublish(charp); + erl_err_msg(charp); + erl_err_quit(charp); + erl_err_ret(charp); + erl_err_sys(charp); + + erl_cons(etermp,etermp); + erl_copy_term(etermp); + erl_element(intx,etermp); + + erl_hd(etermp); + erl_iolist_to_binary(etermp); + erl_iolist_to_string(etermp); + erl_iolist_length(etermp); + erl_length(etermp); + erl_mk_atom(charp); + erl_mk_binary(charp,intx); + erl_mk_empty_list(); + erl_mk_estring(charp, intx); + erl_mk_float(doublex); + erl_mk_int(intx); + erl_mk_list(etermpp,intx); + erl_mk_pid(charp,uintx,uintx,uchar); + erl_mk_port(charp,uintx,uchar); + erl_mk_ref(charp,uintx,uchar); + erl_mk_long_ref(charp,uintx,uintx,uintx,uchar); + erl_mk_string(charp); + erl_mk_tuple(etermpp,intx); + erl_mk_uint(uintx); + erl_mk_var(charp); + erl_print_term(filep,etermp); + /* erl_sprint_term(charp,etermp); */ + erl_size(etermp); + erl_tl(etermp); + erl_var_content(etermp, charp); + + erl_format(charp); + erl_match(etermp, etermp); + + erl_global_names(intx, intp); + erl_global_register(intx, charp, etermp); + erl_global_unregister(intx, charp); + erl_global_whereis(intx, charp, charp); + + erl_init_malloc(erl_heapp,longx); + erl_alloc_eterm(uchar); + erl_eterm_release(); + erl_eterm_statistics(ulongp,ulongp); + erl_free_array(etermpp,intx); + erl_free_term(etermp); + erl_free_compound(etermp); + erl_malloc(longx); + erl_free(voidp); + + erl_compare_ext(ucharp, ucharp); + erl_decode(ucharp); + erl_decode_buf(ucharpp); + erl_encode(etermp,ucharp); + erl_encode_buf(etermp,ucharpp); + erl_ext_size(ucharp); + erl_ext_type(ucharp); + erl_peek_ext(ucharp,intx); + erl_term_len(etermp); + + erl_gethostbyname(charp); + erl_gethostbyaddr(charp, intx, intx); + erl_gethostbyname_r(charp, hostp, charp, intx, intp); + erl_gethostbyaddr_r(charp, intx, intx, hostp, charp, intx, intp); + + erl_init_resolve(); + erl_distversion(intx); + + erl_epmd_connect(inaddrp); + erl_epmd_port(inaddrp, charp, intp); + + charp = ERL_ATOM_PTR(etermp); + intx = ERL_ATOM_SIZE(etermp); + ucharp = ERL_BIN_PTR(etermp); + intx = ERL_BIN_SIZE(etermp); + etermp = ERL_CONS_HEAD(etermp); + etermp = ERL_CONS_TAIL(etermp); + intx = ERL_COUNT(etermp); + doublex= ERL_FLOAT_VALUE(etermp); + uintx = ERL_INT_UVALUE(etermp); + intx = ERL_INT_VALUE(etermp); + intx = ERL_IS_ATOM(etermp); + intx = ERL_IS_BINARY(etermp); + intx = ERL_IS_CONS(etermp); + intx = ERL_IS_EMPTY_LIST(etermp); + intx = ERL_IS_FLOAT(etermp); + intx = ERL_IS_INTEGER(etermp); + intx = ERL_IS_LIST(etermp); + intx = ERL_IS_PID(etermp); + intx = ERL_IS_PORT(etermp); + intx = ERL_IS_REF(etermp); + intx = ERL_IS_TUPLE(etermp); + intx = ERL_IS_UNSIGNED_INTEGER(etermp); + uchar = ERL_PID_CREATION(etermp); + charp = ERL_PID_NODE(etermp); + uintx = ERL_PID_NUMBER(etermp); + uintx = ERL_PID_SERIAL(etermp); + uchar = ERL_PORT_CREATION(etermp); + charp = ERL_PORT_NODE(etermp); + uintx = ERL_PORT_NUMBER(etermp); + uchar = ERL_REF_CREATION(etermp); + intx = ERL_REF_LEN(etermp); + charp = ERL_REF_NODE(etermp); + uintx = ERL_REF_NUMBER(etermp); + uintp = ERL_REF_NUMBERS(etermp); + etermp = ERL_TUPLE_ELEMENT(etermp,intx); + intx = ERL_TUPLE_SIZE(etermp); + + return + BUFSIZ + + EAGAIN + + EHOSTUNREACH + + EINVAL + + EIO + + EMSGSIZE + + ENOMEM + + ERL_ATOM + + ERL_BINARY + + ERL_ERROR + + ERL_EXIT + + ERL_FLOAT + + ERL_INTEGER + + ERL_LINK + + ERL_LIST + + ERL_MSG + + ERL_NO_TIMEOUT + + ERL_PID + + ERL_PORT + + ERL_REF + + ERL_REG_SEND + + ERL_SEND + + ERL_SMALL_BIG + + ERL_TICK + + ERL_TIMEOUT + + ERL_TUPLE + + ERL_UNLINK + + ERL_U_INTEGER + + ERL_U_SMALL_BIG + + ERL_VARIABLE + + ETIMEDOUT + + MAXNODELEN + + MAXREGLEN; +} diff --git a/lib/erl_interface/src/prog/erl_start.c b/lib/erl_interface/src/prog/erl_start.c new file mode 100644 index 0000000000..a53aab9ac7 --- /dev/null +++ b/lib/erl_interface/src/prog/erl_start.c @@ -0,0 +1,735 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-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% + * + + */ + +/* An exception from using eidef.h, use config.h directly */ +#include "config.h" + +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> + +#ifdef __WIN32__ +#include <winsock2.h> +#include <windows.h> +#include <winbase.h> + +#elif VXWORKS +#include <stdio.h> +#include <string.h> +#include <vxWorks.h> +#include <hostLib.h> +#include <selectLib.h> +#include <ifLib.h> +#include <sockLib.h> +#include <taskLib.h> +#include <inetLib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <symLib.h> +#include <sysSymTbl.h> +#include <sysLib.h> +#include <tickLib.h> + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#include <a_out.h> + +/* #include "netdb.h" */ +#else /* other unix */ +#include <errno.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <time.h> +#include <unistd.h> +#include <sys/types.h> +#include <signal.h> +#endif + +#include "ei.h" +#include "ei_resolve.h" +#include "erl_start.h" + +/* FIXME is this a case a vfork can be used? */ +#if !HAVE_WORKING_VFORK +# define vfork fork +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#ifndef RSH +#define RSH "/usr/bin/rsh" +#endif + +#ifndef HAVE_SOCKLEN_T +typedef int SocklenType; +#else +typedef socklen_t SocklenType; +#endif + +/* FIXME check errors from malloc */ + +static struct in_addr *get_addr(const char *hostname, struct in_addr *oaddr); + +static int wait_for_erlang(int sockd, int magic, struct timeval *timeout); +#if defined(VXWORKS) || defined(__WIN32__) +static int unique_id(void); +static unsigned long spawn_erlang_epmd(ei_cnode *ec, + char *alive, + Erl_IpAddr adr, + int flags, + char *erl_or_epmd, + char *args[], + int port, + int is_erlang); +#else +static int exec_erlang(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags, + char *erl, char *args[],int port); +#endif +/* Start an Erlang node. return value 0 indicates that node was + * started successfully, negative values indicate error. + * + * node - the name of the remote node to start (alivename@hostname). + * flags - turn on or off certain options. See erl_start.h for a list. + * erl - is the name of the erl script to call. If NULL, the default + * name "erl" will be used. + * args - a NULL-terminated list of strings containing + * additional arguments to be sent to the remote Erlang node. These + * strings are simply appended to the end of the command line, so any + * quoting of special characters, etc must be done by the caller. + * There may be some conflicts between some of these arguments and the + * default arguments hard-coded into this function, so be careful. + */ +int erl_start_sys(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags, + char *erl, char *args[]) +{ + struct timeval timeout; + struct sockaddr_in addr; + SocklenType namelen; + int port; + int sockd = 0; + int one = 1; +#if defined(VXWORKS) || defined(__WIN32__) + unsigned long pid = 0; +#else + int pid = 0; +#endif + int r = 0; + + if (((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) || + (setsockopt(sockd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0)) { + r = ERL_SYS_ERROR; + goto done; + } + + memset(&addr,0,sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = 0; + + if (bind(sockd,(struct sockaddr *)&addr,sizeof(addr))<0) { + return ERL_SYS_ERROR; + } + namelen = sizeof(addr); + if (getsockname(sockd,(struct sockaddr *)&addr,&namelen)<0) { + return ERL_SYS_ERROR; + } + port = ntohs(addr.sin_port); + + listen(sockd,5); + +#if defined(VXWORKS) || defined(__WIN32__) + if((pid = spawn_erlang_epmd(ec,alive,adr,flags,erl,args,port,1)) + == 0) + return ERL_SYS_ERROR; + timeout.tv_usec = 0; + timeout.tv_sec = 10; /* ignoring ERL_START_TIME */ + if((r = wait_for_erlang(sockd,unique_id(),&timeout)) + == ERL_TIMEOUT) { +#if defined(VXWORKS) + taskDelete((int) pid); + if(taskIdVerify((int) pid) != ERROR) + taskDeleteForce((int) pid); +#else /* Windows */ + /* Well, this is not a nice way to do it, and it does not + always kill the emulator, but the alternatives are few.*/ + TerminateProcess((HANDLE) pid,1); +#endif /* defined(VXWORKS) */ + } +#else /* Unix */ + switch ((pid = fork())) { + case -1: + r = ERL_SYS_ERROR; + break; + + case 0: + /* child - start the erlang node */ + exec_erlang(ec, alive, adr, flags, erl, args, port); + + /* error if reached - parent reports back to caller after timeout + so we just exit here */ + exit(1); + break; + + default: + + /* parent - waits for response from Erlang node */ + /* child pid used here as magic number */ + timeout.tv_usec = 0; + timeout.tv_sec = 10; /* ignoring ERL_START_TIME */ + if ((r = wait_for_erlang(sockd,pid,&timeout)) == ERL_TIMEOUT) { + /* kill child if no response */ + kill(pid,SIGINT); + sleep(1); + if (waitpid(pid,NULL,WNOHANG) != pid) { + /* no luck - try harder */ + kill(pid,SIGKILL); + sleep(1); + waitpid(pid,NULL,WNOHANG); + } + } + + } +#endif /* defined(VXWORKS) || defined(__WIN32__) */ + +done: +#if defined(__WIN32__) + if (sockd) closesocket(sockd); +#else + if (sockd) close(sockd); +#endif + return r; +} /* erl_start_sys() */ + +#if defined(VXWORKS) || defined(__WIN32__) +#if defined(VXWORKS) +#define DEF_ERL_COMMAND "" +#define DEF_EPMD_COMMAND "" +#define ERLANG_SYM "start_erl" +#define EPMD_SYM "start_epmd" +#define ERL_REPLY_FMT "-s erl_reply reply %s %d %d" +#else +#define DEF_ERL_COMMAND "erl" +#define DEF_EPMD_COMMAND "epmd" +#define ERL_REPLY_FMT "-s erl_reply reply \"%s\" \"%d\" \"%d\"" +#endif +#define ERL_NAME_FMT "-noinput -name %s" +#define ERL_SNAME_FMT "-noinput -sname %s" + +#define IP_ADDR_CHARS 15 +#define FORMATTED_INT_LEN 10 + +static int unique_id(void){ +#if defined(VXWORKS) + return taskIdSelf(); +#else + return (int) GetCurrentThreadId(); +#endif +} + +static int enquote_args(char **oargs, char ***qargs){ + char **args; + int len; + int i; + int qwhole; + int extra; + char *ptr; + char *ptr2; + + if(oargs == NULL){ + *qargs = malloc(sizeof(char *)); + **qargs = NULL; + return 0; + }; + + for(len=0;oargs[len] != NULL; ++len) + ; + args = malloc(sizeof(char *) * (len + 1)); + + for(i = 0; i < len; ++i){ + qwhole = strchr(oargs[i],' ') != NULL; + extra = qwhole * 2; + for(ptr = oargs[i]; *ptr != '\0'; ++ptr) + extra += (*ptr == '"'); + args[i] = malloc(strlen(oargs[i]) + + extra + + 1); + ptr2 = args[i]; + if(qwhole) + *(ptr2++) = '"'; + for(ptr = oargs[i]; *ptr != '\0'; ++ptr){ + if(*ptr == '"') + *(ptr2++) = '\\'; + *(ptr2++) = *ptr; + } + if(qwhole) + *(ptr2++) = '"'; + *ptr2 = '\0'; + } + args[len] = NULL; + *qargs = args; + return len; +} + +static void free_args(char **args){ + char **ptr = args; + while(*ptr != NULL) + free(*(ptr++)); + free(args); +} + +#if defined(VXWORKS) +static FUNCPTR lookup_function(char *symname){ + char *value; + SYM_TYPE type; + if(symFindByName(sysSymTbl, + symname, + &value, + &type) == ERROR /*|| type != N_TEXT*/) + return NULL; + return (FUNCPTR) value; +} +#endif /* defined(VXWORKS) */ + +/* In NT and VxWorks, we cannot fork(), Erlang and Epmd gets + spawned by this function instead. */ + +static unsigned long spawn_erlang_epmd(ei_cnode *ec, + char *alive, + Erl_IpAddr adr, + int flags, + char *erl_or_epmd, + char *args[], + int port, + int is_erlang) +{ +#if defined(VXWORKS) + FUNCPTR erlfunc; +#else /* Windows */ + STARTUPINFO sinfo; + SECURITY_ATTRIBUTES sa; + PROCESS_INFORMATION pinfo; +#endif + char *cmdbuf; + int cmdlen; + char *ptr; + int i; + int num_args; + char *name_format; + struct in_addr myaddr; + struct in_addr *hisaddr = (struct in_addr *)adr; + char iaddrbuf[IP_ADDR_CHARS + 1]; + int ret; + + if(is_erlang){ + get_addr(ei_thishostname(ec), &myaddr); +#if defined(VXWORKS) + inet_ntoa_b(myaddr, iaddrbuf); +#else /* Windows */ + if((ptr = inet_ntoa(myaddr)) == NULL) + return 0; + else + strcpy(iaddrbuf,ptr); +#endif + } + if ((flags & ERL_START_REMOTE) || + (is_erlang && (hisaddr->s_addr != myaddr.s_addr))) { + return 0; + } else { + num_args = enquote_args(args, &args); + for(cmdlen = i = 0; args[i] != NULL; ++i) + cmdlen += strlen(args[i]) + 1; +#if !defined(VXWORKS) + /* On VxWorks, we dont actually run a command, + we call start_erl() */ + if(!erl_or_epmd) +#endif + erl_or_epmd = (is_erlang) ? DEF_ERL_COMMAND : + DEF_EPMD_COMMAND; + if(is_erlang){ + name_format = (flags & ERL_START_LONG) ? ERL_NAME_FMT : + ERL_SNAME_FMT; + cmdlen += + strlen(erl_or_epmd) + (*erl_or_epmd != '\0') + + strlen(name_format) + 1 + strlen(alive) + + strlen(ERL_REPLY_FMT) + 1 + strlen(iaddrbuf) + + 2 * FORMATTED_INT_LEN + + 1; + ptr = cmdbuf = malloc(cmdlen); + if(*erl_or_epmd != '\0') + ptr += sprintf(ptr,"%s ",erl_or_epmd); + ptr += sprintf(ptr, name_format, + alive); + ptr += sprintf(ptr, " " ERL_REPLY_FMT, + iaddrbuf, port, unique_id()); + } else { /* epmd */ + cmdlen += strlen(erl_or_epmd) + (*erl_or_epmd != '\0') + 1; + ptr = cmdbuf = malloc(cmdlen); + if(*erl_or_epmd != '\0') + ptr += sprintf(ptr,"%s ",erl_or_epmd); + else + *(ptr++) = '\0'; + } + for(i= 0; args[i] != NULL; ++i){ + *(ptr++) = ' '; + strcpy(ptr,args[i]); + ptr += strlen(args[i]); + } + free_args(args); + if (flags & ERL_START_VERBOSE) { + fprintf(stderr,"erl_call: commands are %s\n",cmdbuf); + } + /* OK, one single command line... */ +#if defined(VXWORKS) + erlfunc = lookup_function((is_erlang) ? ERLANG_SYM : + EPMD_SYM); + if(erlfunc == NULL){ + if (flags & ERL_START_VERBOSE) { + fprintf(stderr,"erl_call: failed to find symbol %s\n", + (is_erlang) ? ERLANG_SYM : EPMD_SYM); + } + ret = 0; + } else { + /* Just call it, it spawns itself... */ + ret = (unsigned long) + (*erlfunc)((int) cmdbuf,0,0,0,0,0,0,0,0,0); + if(ret == (unsigned long) ERROR) + ret = 0; + } +#else /* Windows */ + /* Hmmm, hidden or unhidden window??? */ + memset(&sinfo,0,sizeof(sinfo)); + sinfo.cb = sizeof(STARTUPINFO); + sinfo.dwFlags = STARTF_USESHOWWINDOW /*| + STARTF_USESTDHANDLES*/; + sinfo.wShowWindow = SW_HIDE; /* Hidden! */ + sinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + sinfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + sinfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = /*TRUE*/ FALSE; + if(!CreateProcess( + NULL, + cmdbuf, + &sa, + NULL, + /*TRUE*/ FALSE, + 0 | CREATE_NEW_CONSOLE, + NULL, + NULL, + &sinfo, + &pinfo)) + ret = 0; + else + ret = (unsigned long) pinfo.hProcess; +#endif + free(cmdbuf); + return ret; + } + /* NOTREACHED */ +} +#else /* Unix */ + +/* call this from the child process to start an erlang system. This + * function just builds the erlang command line and then calls it. + * + * node - the nodename for the new node + * flags - various options that can be set (see erl_start.h) + * erl - name of the erlang executable, or NULL for default ("erl") + * args - additional arguments to pass to erlang executable + * port - the port number where we wait for acknowledgment from the enode + * + * we have a potential problem if args conflicts with any of the + * arguments we use here. + */ +static int exec_erlang(ei_cnode *ec, + char *alive, + Erl_IpAddr adr, + int flags, + char *erl, + char *args[], + int port) +{ +#if !defined(__WIN32__) && !defined(VXWORKS) + int fd,len,l,i; + char **s; + char *argv[4]; + char argbuf[BUFSIZ]; + struct in_addr myaddr; + struct in_addr *hisaddr = (struct in_addr *)adr; + + get_addr(ei_thishostname(ec), &myaddr); + + /* on this host? */ + /* compare ip addresses, unless forced by flag setting to use rsh */ + if ((flags & ERL_START_REMOTE) || (hisaddr->s_addr != myaddr.s_addr)) { + argv[0] = RSH; + len = strlen(inet_ntoa(*hisaddr)); + argv[1] = malloc(len+1); + strcpy(argv[1],inet_ntoa(*hisaddr)); + } + else { + /* Yes - use sh to start local Erlang */ + argv[0] = "sh"; + argv[1] = "-c"; + } + argv[2] = argbuf; + argv[3] = NULL; + + len = 0; + *argbuf=(char)0; + + sprintf(argbuf,"exec %s ", (erl? erl: "erl")); + len = strlen(argbuf); + + /* *must* be noinput or node (seems to) hang... */ + /* long or short names? */ + sprintf(&argbuf[len], "-noinput %s %s ", + ((flags & ERL_START_LONG) ? "-name" : "-sname"), + alive); + len = strlen(argbuf); + + /* now make the new node report back when it's ready */ + /* add: myip, myport and replymsg */ + sprintf(&argbuf[len], + "-s erl_reply reply %s %d %d ", + inet_ntoa(myaddr),port,(int)getpid()); +#ifdef DEBUG + fprintf(stderr,"erl_call: debug %s\n",&argbuf[len]); +#endif + len = strlen(argbuf); + + /* additional arguments to be passed to the other system */ + /* make sure that they will fit first */ + for (l=0, s = args; s && *s; s++) l+= strlen(*s) + 1; + + if (len + l + 1 > BUFSIZ) return ERL_BADARG; + else { + for (s = args; s && *s; s++) { + strcat(argbuf," "); + strcat(argbuf,*s); + } + len += l + 1; + } + + if (flags & ERL_START_VERBOSE) { + fprintf(stderr,"erl_call: %s %s %s\n",argv[0],argv[1],argv[2]); + } + + /* close all descriptors in child */ + for (i=0; i<64; i++) close(i); + + /* debug output to file? */ + if (flags & ERL_START_DEBUG) { + char debugfile[MAXPATHLEN+1]; + char *home=getenv("HOME"); + sprintf(debugfile,"%s/%s.%s",home,ERL_START_LOGFILE,alive); + if ((fd=open(debugfile, O_WRONLY | O_CREAT | O_APPEND, 0644)) >= 0) { + time_t t = time(NULL); + dup2(fd,1); + dup2(fd,2); + fprintf(stderr,"\n\n===== Log started ======\n%s \n",ctime(&t)); + fprintf(stderr,"erl_call: %s %s %s\n",argv[0],argv[1],argv[2]); + } + } + + /* start the system */ + execvp(argv[0], argv); + + if (flags & ERL_START_DEBUG) { + fprintf(stderr,"erl_call: exec failed: (%d) %s %s %s\n", + errno,argv[0],argv[1],argv[2]); + } + +#endif + /* (hopefully) NOT REACHED */ + return ERL_SYS_ERROR; +} /* exec_erlang() */ + +#endif /* defined(VXWORKS) || defined(WINDOWS) */ + +#if defined(__WIN32__) +static void gettimeofday(struct timeval *now,void *dummy){ + SYSTEMTIME systime; + FILETIME ft; + DWORD x; + GetSystemTime(&systime); + SystemTimeToFileTime(&systime,&ft); + x = ft.dwLowDateTime / 10; + now->tv_sec = x / 1000000; + now->tv_usec = x % 1000000; +} + +#elif defined(VXWORKS) +static void gettimeofday(struct timeval *now, void *dummy){ + int rate = sysClkRateGet(); /* Ticks per second */ + unsigned long ctick = tickGet(); + now->tv_sec = ctick / rate; /* secs since reboot */ + now->tv_usec = ((ctick - (now->tv_sec * rate))*1000000)/rate; +} +#endif + + +/* wait for the remote system to reply */ +/* + * sockd - an open socket where we expect a connection from the e-node + * magic - sign on message the e-node must provide for verification + * timeout - how long to wait before returning failure + * + * OBS: the socket is blocking, and there is a potential deadlock if we + * get an accept but the peer sends no data (and does not close). + * in normal cases the timeout will work ok however, i.e. either we + * never get any connection, or we get connection then close(). + */ +static int wait_for_erlang(int sockd, int magic, struct timeval *timeout) +{ + struct timeval to; + struct timeval stop_time; + struct timeval now; + fd_set rdset; + int fd; + int n,i; + char buf[16]; + struct sockaddr_in peer; + SocklenType len = (SocklenType) sizeof(peer); + + /* determine when we should exit this function */ + gettimeofday(&now,NULL); + stop_time.tv_sec = now.tv_sec + timeout->tv_sec; + stop_time.tv_usec = now.tv_usec + timeout->tv_usec; + while (stop_time.tv_usec > 1000000) { + stop_time.tv_sec++; + stop_time.tv_usec -= 1000000; + } + +#ifdef DEBUG + fprintf(stderr,"erl_call: debug time is %ld.%06ld, " + "will timeout at %ld.%06ld\n", + now.tv_sec,now.tv_usec,stop_time.tv_sec,stop_time.tv_usec); +#endif + + while (1) { + FD_ZERO(&rdset); + FD_SET(sockd,&rdset); + + /* adjust the timeout to (stoptime - now) */ + gettimeofday(&now,NULL); + to.tv_sec = stop_time.tv_sec - now.tv_sec; + to.tv_usec = stop_time.tv_usec - now.tv_usec; + while ((to.tv_usec <= 0) && (to.tv_sec >= 0)) { + to.tv_usec += 1000000; + to.tv_sec--; + } + if (to.tv_sec < 0) return ERL_TIMEOUT; + +#ifdef DEBUG + fprintf(stderr,"erl_call: debug remaining to timeout: %ld.%06ld\n", + to.tv_sec,to.tv_usec); +#endif + switch ((i = select(sockd+1,&rdset,NULL,NULL,&to))) { + case -1: + return ERL_SYS_ERROR; + break; + + case 0: /* timeout */ +#ifdef DEBUG + gettimeofday(&now,NULL); + fprintf(stderr,"erl_call: debug timed out at %ld.%06ld\n", + now.tv_sec,now.tv_usec); +#endif + return ERL_TIMEOUT; + break; + + default: /* ready descriptors */ +#ifdef DEBUG + gettimeofday(&now,NULL); + fprintf(stderr,"erl_call: debug got select at %ld.%06ld\n", + now.tv_sec,now.tv_usec); +#endif + if (FD_ISSET(sockd,&rdset)) { + if ((fd = accept(sockd,(struct sockaddr *)&peer,&len)) < 0) + return ERL_SYS_ERROR; + + /* now get sign-on message and terminate it */ +#if defined(__WIN32__) + if ((n=recv(fd,buf,16,0)) >= 0) buf[n]=0x0; + closesocket(fd); +#else + if ((n=read(fd,buf,16)) >= 0) buf[n]=0x0; + close(fd); +#endif +#ifdef DEBUG + fprintf(stderr,"erl_call: debug got %d, expected %d\n", + atoi(buf),magic); +#endif + + if (atoi(buf) == magic) return 0; /* success */ + } /* if FD_SET */ + } /* switch */ + } /* while */ + + /* unreached? */ + return ERL_SYS_ERROR; +} /* wait_for_erlang() */ + + +static struct in_addr *get_addr(const char *hostname, struct in_addr *oaddr) +{ + struct hostent *hp; + +#if !defined (__WIN32__) + char buf[1024]; + struct hostent host; + int herror; + + hp = ei_gethostbyname_r(hostname,&host,buf,1024,&herror); +#else + hp = ei_gethostbyname(hostname); +#endif + + if (hp) { + memmove(oaddr,hp->h_addr_list[0],sizeof(*oaddr)); + return oaddr; + } + return NULL; +} diff --git a/lib/erl_interface/src/prog/erl_start.h b/lib/erl_interface/src/prog/erl_start.h new file mode 100644 index 0000000000..05f34864a7 --- /dev/null +++ b/lib/erl_interface/src/prog/erl_start.h @@ -0,0 +1,46 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-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% + * + + */ +#ifndef _ERL_START_H +#define _ERL_START_H + +#define ERL_START_MSG "gurka" /* make something up */ +#define ERL_START_TIME 10000 /* wait this long (ms) */ +#define ERL_START_LOGFILE ".erl_start.out" /* basename of logfile */ + +/* flags used by erl_connect and erl_xconnect */ +#define ERL_START_ENODE 0x0001 +#define ERL_START_EPMD 0x0002 +#define ERL_START_LONG 0x0004 +#define ERL_START_COOKIE 0x0008 +#define ERL_START_DEBUG 0x0010 +#define ERL_START_VERBOSE 0x0020 +#define ERL_START_REMOTE 0x0040 + +/* error return values */ +#define ERL_S_TIMEOUT -51 /* a timeout occurred */ +#define ERL_BADARG -52 /* an argument contained an incorrect value */ +#define ERL_SYS_ERROR -99 /* a system error occurred (check errno) */ + +/* start an erlang system */ +int erl_start_sys(ei_cnode *ec, char *alive, Erl_IpAddr addr, int flags, + char *erl, char *add_args[]); + +#endif /* _ERL_START_H */ |