/* * %CopyrightBegin% * * Copyright Ericsson AB 1996-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * %CopyrightEnd% */ /* * Purpose: Connect to any node at any host. */ /*************************************************************************** * * 'erl_interface' node connection handling is to use 'ei' for all * operations without access to the internal structure of saved data, * e.i. it should use the public interface functions. The connection * handling can be seen as a restricted node interface where only one * node can be used in one operating system process. * ***************************************************************************/ #include "eidef.h" #include #include #include #ifdef __WIN32__ #include #include #include #elif VXWORKS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "erl_error.h" #else /* some other unix */ #include #include #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include #include #include #include #include #include /* for gen_challenge (NEED FIX?) */ #endif /* common includes */ #include #include #include #include /* FIXME include less */ #include "erl_interface.h" #include "erl_connect.h" #include "erl_eterm.h" #include "erl_malloc.h" #include "putget.h" #include "ei.h" #include "ei_connect_int.h" #include "ei_locking.h" #include "ei_epmd.h" #include "ei_internal.h" /* rpc_from() uses a buffer this size */ #ifndef MAX_RECEIVE_BUF #define MAX_RECEIVE_BUF 32*1024 #endif /* This is the global state of the old erl_* API */ static ei_cnode erl_if_ec; /*************************************************************************** * * API: erl_connect_init() * API: erl_connect_xinit() * * Returns 1 on success and 0 on failure. * Not documented to set erl_errno. * ***************************************************************************/ int erl_connect_init(int this_node_number, char *cookie, short creation) { char nn[MAXATOMLEN]; sprintf(nn, "c%d", this_node_number); return ei_connect_init(&erl_if_ec, nn, cookie, creation) == 0; } /* FIXME documented to use struct in_addr as addr */ int erl_connect_xinit(char *thishostname, char *thisalivename, char *thisnodename, struct in_addr *thisipaddr, char *cookie, short creation) { return ei_connect_xinit(&erl_if_ec, thishostname, thisalivename, thisnodename, thisipaddr, cookie, creation) >= 0; } /*************************************************************************** * * API: erl_connect() * API: erl_xconnect() * * Set up a connection to a given Node, and interchange hand shake * messages with it. * * Returns valid file descriptor on success and < 0 on failure. * Set erl_errno to EHOSTUNREACH, ENOMEM, EIO or errno from socket(2) * or connect(2). * ***************************************************************************/ int erl_connect(char *nodename) { int res = ei_connect(&erl_if_ec, nodename); if (res < 0) erl_errno = EIO; return res; } /* FIXME documented to use struct in_addr as addr */ int erl_xconnect(Erl_IpAddr addr, char *alivename) { return ei_xconnect(&erl_if_ec, addr, alivename); } /*************************************************************************** * * API: erl_close_connection() * * Returns 0 on success and -1 on failure. * ***************************************************************************/ int erl_close_connection(int fd) { return ei_close_connection(fd); } /* * Accept and initiate a connection from another * Erlang node. Return a file descriptor at success, * otherwise -1; */ int erl_accept(int lfd, ErlConnect *conp) { return ei_accept(&erl_if_ec, lfd, conp); } /* Receives a message from an Erlang socket. * If the message was a TICK it is immediately * answered. Returns: ERL_ERROR, ERL_TICK or * the number of bytes read. */ int erl_receive(int s, unsigned char *bufp, int bufsize) { return ei_receive(s, bufp, bufsize); } /* * Send an Erlang message to a registered process * at the Erlang node, connected with a socket. */ int erl_reg_send(int fd, char *server_name, ETERM *msg) { ei_x_buff x; int r; if (ei_x_new_with_version(&x) < 0) { erl_errno = ENOMEM; return 0; } if (ei_x_encode_term(&x, msg) < 0) { erl_errno = EINVAL; r = 0; } else { r = ei_reg_send(&erl_if_ec, fd, server_name, x.buff, x.index); } ei_x_free(&x); return r == 0; } /* * Sends an Erlang message to a process at an Erlang node */ int erl_send(int fd, ETERM *to ,ETERM *msg) { erlang_pid topid; ei_x_buff x; int r; ei_x_new_with_version(&x); ei_x_encode_term(&x, msg); /* make the to-pid */ if (!ERL_IS_PID(to)) { ei_x_free(&x); erl_errno = EINVAL; return -1; } if (to->uval.pidval.node.latin1) { strcpy(topid.node, to->uval.pidval.node.latin1); } else { strcpy(topid.node, to->uval.pidval.node.utf8); } topid.num = ERL_PID_NUMBER(to); topid.serial = ERL_PID_SERIAL(to); topid.creation = ERL_PID_CREATION(to); r = ei_send(fd, &topid, x.buff, x.index); ei_x_free(&x); return r == 0; } static int erl_do_receive_msg(int fd, ei_x_buff* x, ErlMessage* emsg) { erlang_msg msg; int r; msg.from.node[0] = msg.to.node[0] = msg.toname[0] = '\0'; r = ei_do_receive_msg(fd, 0, &msg, x, 0); if (r == ERL_MSG) { int index = 0; emsg->type = msg.msgtype; /* We can't call ei_decode_term for cases where there are no data following the type information. If there are other types added later where there are data this case has to be extended. */ switch (msg.msgtype) { case ERL_SEND: case ERL_REG_SEND: case ERL_EXIT: case ERL_EXIT2: if (ei_decode_term(x->buff, &index, &emsg->msg) < 0) r = ERL_ERROR; break; default: emsg->msg = NULL; /* Not needed but may avoid problems for unsafe caller */ break; } } else emsg->msg = NULL; if (msg.from.node[0] != '\0') emsg->from = erl_mk_pid(msg.from.node, msg.from.num, msg.from.serial, msg.from.creation); else emsg->from = NULL; if (msg.to.node[0] != '\0') emsg->to = erl_mk_pid(msg.to.node, msg.to.num, msg.to.serial, msg.to.creation); else emsg->to = NULL; strcpy(emsg->to_name, msg.toname); return r; } int erl_receive_msg(int fd, unsigned char *buf, int bufsize, ErlMessage *emsg) { ei_x_buff x; int r; ei_x_new(&x); r = erl_do_receive_msg(fd, &x, emsg); /* FIXME what is this about? */ if (bufsize > x.index) bufsize = x.index; memcpy(buf, x.buff, bufsize); ei_x_free(&x); return r; } int erl_xreceive_msg(int fd, unsigned char **buf, int *bufsize, ErlMessage *emsg) { ei_x_buff x; int r; ei_x_new(&x); r = erl_do_receive_msg(fd, &x, emsg); if (*bufsize < x.index) *buf = erl_realloc(*buf, x.index); *bufsize = x.index; memcpy(*buf, x.buff, *bufsize); ei_x_free(&x); return r; } /* * The RPC consists of two parts, send and receive. * Here is the send part ! * { PidFrom, { call, Mod, Fun, Args, user }} */ /* * Now returns non-negative number for success, negative for failure. */ int erl_rpc_to(int fd, char *mod, char *fun, ETERM *args) { int r; ei_x_buff x; ei_x_new(&x); ei_x_encode_term(&x, args); r = ei_rpc_to(&erl_if_ec, fd, mod, fun, x.buff, x.index); ei_x_free(&x); return r; } /* rpc_to */ /* * And here is the rpc receiving part. A negative * timeout means 'infinity'. Returns either of: ERL_MSG, * ERL_TICK, ERL_ERROR or ERL_TIMEOUT. */ int erl_rpc_from(int fd, int timeout, ErlMessage *emsg) { fd_set readmask; struct timeval tv; struct timeval *t = NULL; unsigned char rbuf[MAX_RECEIVE_BUF]; if (timeout >= 0) { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; t = &tv; } FD_ZERO(&readmask); FD_SET(fd,&readmask); switch (select(fd+1, &readmask, NULL, NULL, t)) { case -1: erl_errno = EIO; return ERL_ERROR; case 0: erl_errno = ETIMEDOUT; return ERL_TIMEOUT; default: if (FD_ISSET(fd, &readmask)) return erl_receive_msg(fd, rbuf, MAX_RECEIVE_BUF, emsg); else { erl_errno = EIO; return ERL_ERROR; } } } /* rpc_from */ /* * A true RPC. It return a NULL pointer * in case of failure, otherwise a valid * (ETERM *) pointer containing the reply */ ETERM *erl_rpc(int fd, char *mod, char *fun, ETERM *args) { int i; ETERM *ep; ErlMessage emsg; if (erl_rpc_to(fd, mod, fun, args) < 0) { return NULL; } while ((i=erl_rpc_from(fd, ERL_NO_TIMEOUT, &emsg)) == ERL_TICK); if (i == ERL_ERROR) return NULL; ep = erl_element(2,emsg.msg); /* {RPC_Tag, RPC_Reply} */ erl_free_term(emsg.msg); erl_free_term(emsg.to); return ep; } /* rpc */ /* ** Handshake */ int erl_publish(int port) { return ei_publish(&erl_if_ec, port); } int erl_unpublish(const char *alive) { return ei_unpublish_tmo(alive,0); } erlang_pid *erl_self(void) { return ei_self(&erl_if_ec); } const char *erl_thisnodename(void) { return ei_thisnodename(&erl_if_ec); } const char *erl_thishostname(void) { return ei_thishostname(&erl_if_ec); } const char *erl_thisalivename(void) { return ei_thisalivename(&erl_if_ec); } const char *erl_thiscookie(void) { return ei_thiscookie(&erl_if_ec); } short erl_thiscreation(void) { return ei_thiscreation(&erl_if_ec); }