> ## 7 C Nodes

> This is an example of how to solve the example problem by using a C node. Note that a C node would not typically be used for solving a simple problem like this, a port would suffice.

> ### 7.1 Erlang Program

> From Erlang's point of view, the C node is treated like a normal Erlang node. Therefore, calling the functions `foo` and `bar` only involves sending a message to the C node asking for the function to be called, and receiving the result. Sending a message requires a recipient; a process which can be defined using either a pid or a tuple consisting of a registered name and a node name. In this case a tuple is the only alternative as no pid is known.

>     {RegName, Node} ! Msg

> The node name `Node` should be the name of the C node. If short node names are used, the plain name of the node will be `cN` where `N` is an integer. If long node names are used, there is no such restriction. An example of a C node name using short node names is thus `c1@idril`, an example using long node names is `cnode@idril.ericsson.se`.

> The registered name `RegName` could be any atom. The name can be ignored by the C code, or it could be used for example to distinguish between different types of messages. Below is an example of what the Erlang code could look like when using short node names, complex3.erl.

>     -module(complex3).
>     -export([foo/1, bar/1]).

>     foo(X) ->
>         call_cnode({foo, X}).
>     bar(Y) ->
>         call_cnode({bar, Y}).

>     call_cnode(Msg) ->
>         {any, c1@idril} ! {call, self(), Msg},
>         receive
>             {cnode, Result} ->
>                 Result
>         end.

> In complex4.erl. there is an example of what the Erlang code could look like when using long node names.

> ### 7.2 C Program

> #### 7.2.1 Setting Up the Communication

> Before calling any other Erl_Interface function, the memory handling must be initiated.

>     erl_init(NULL, 0);

> Now the C node can be initiated. If short node names are used, this is done by calling `erl_connect_init()`.

>     erl_connect_init(1, "secretcookie", 0);

> The first argument is the integer which is used to construct the node name. In the example the plain node name will be `c1`.
> The second argument is a string defining the magic cookie.
> The third argument is an integer which is used to identify a particular instance of a C node.

> If long node node names are used, initiation is done by calling `erl_connect_xinit()`.

>     erl_connect_xinit("idril", "cnode", "cnode@idril.ericsson.se",
>                       &addr, "secretcookie", 0);

> The first three arguments are the host name, the plain node name, and the full node name. The fourth argument is a pointer to an `in_addr` struct with the IP address of the host, and the fifth and sixth arguments are the magic cookie and instance number.

> The C node can act as a server or a client when setting up the communication Erlang-C. If it acts as a client, it connects to an Erlang node by calling `erl_connect()`, which will return an open file descriptor at success.

>     fd = erl_connect("e1@idril");

> If the C node acts as a server, it must first create a socket (call `bind()` and `listen()`) listening to a certain port number `port`. It then publishes its name and port number with `epmd` (the Erlang port mapper daemon, see the man page for `epmd`).

>     erl_publish(port);

> Now the C node server can accept connections from Erlang nodes.

>     fd = erl_accept(listen, &conn);

> The second argument to `erl_accept` is a struct `ErlConnect` that will contain useful information when a connection has been established; for example, the name of the Erlang node.

> #### 7.2.2 Sending and Receiving Messages

> The C node can receive a message from Erlang by calling `erl_receive msg()`. This function reads data from the open file descriptor `fd` into a buffer and puts the result in an `ErlMessage` struct `emsg`. `ErlMessage` has a field `type` defining which kind of data was received. In this case the type of interest is `ERL_REG_SEND` which indicates that Erlang sent a message to a registered process at the C node. The actual message, an `ETERM`, will be in the `msg` field.

> It is also necessary to take care of the types `ERL_ERROR` (an error occurred) and `ERL_TICK` (alive check from other node, should be ignored). Other possible types indicate process events such as link/unlink and exit.

>       while (loop) {

>         got = erl_receive_msg(fd, buf, BUFSIZE, &emsg);
>         if (got == ERL_TICK) {
>           /* ignore */
>         } else if (got == ERL_ERROR) {
>           loop = 0; /* exit while loop */
>         } else {
>           if (emsg.type == ERL_REG_SEND) {

> Since the message is an `ETERM` struct, Erl_Interface functions can be used to manipulate it. In this case, the message will be a 3-tuple (because that was how the Erlang code was written, see above). The second element will be the pid of the caller and the third element will be the tuple `{Function,Arg}` determining which function to call with which argument. The result of calling the function is made into an `ETERM` struct as well and sent back to Erlang using `erl_send()`, which takes the open file descriptor, a pid and a term as arguments.

>             fromp = erl_element(2, emsg.msg);
>             tuplep = erl_element(3, emsg.msg);
>             fnp = erl_element(1, tuplep);
>             argp = erl_element(2, tuplep);

>             if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) {
>               res = foo(ERL_INT_VALUE(argp));
>             } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 3) == 0) {
>               res = bar(ERL_INT_VALUE(argp));
>             }

>             resp = erl_format("{cnode, ~i}", res);
>             erl_send(fd, fromp, resp);

> Finally, the memory allocated by the `ETERM` creating functions (including `erl_receive_msg()` must be freed.

>             erl_free_term(emsg.from); erl_free_term(emsg.msg);
>             erl_free_term(fromp); erl_free_term(tuplep);
>             erl_free_term(fnp); erl_free_term(argp);
>             erl_free_term(resp);

> The resulting C programs can be found in cnode_s.c (C node server, short node names), cnode_s2.c (C node server, long node names) and cnode_c.c (C node client).

> ### 7.3 Running the Example

> 1. Compile the C code, providing the paths to the Erl_Interface include files and libraries, and to the `socket` and `nsl` libraries.

> In R5B and later versions of OTP, the `include` and `lib` directories are situated under `OTPROOT/lib/erl_interface-VSN`, where `OTPROOT` is the root directory of the OTP installation (`/usr/local/otp` in the example above) and `VSN` is the version of the `erl_interface` application (3.2.1 in the example above).
> In R4B and earlier versions of OTP, `include` and `lib` are situated under `OTPROOT/usr`.

>     >  gcc -o cserver \
>     -I/usr/local/otp/lib/erl_interface-3.2.1/include \
>     -L/usr/local/otp/lib/erl_interface-3.2.1/lib \
>     complex.c cnode_s.c \
>     -lerl_interface -lei -lsocket -lnsl

>     unix> gcc -o cserver2 \
>     -I/usr/local/otp/lib/erl_interface-3.2.1/include \
>     -L/usr/local/otp/lib/erl_interface-3.2.1/lib \
>     complex.c cnode_s2.c \
>     -lerl_interface -lei -lsocket -lnsl

>     unix> gcc -o cclient \
>     -I/usr/local/otp/lib/erl_interface-3.2.1/include \
>     -L/usr/local/otp/lib/erl_interface-3.2.1/lib \
>     complex.c cnode_c.c \
>     -lerl_interface -lei -lsocket -lnsl

> 2. Compile the Erlang code.

>     unix> erl -compile complex3 complex4

> 3. Run the C node server example with short node names.

> Start the C program `cserver` and Erlang in different windows. `cserver` takes a port number as argument and must be started before trying to call the Erlang functions. The Erlang node should be given the short name `e1` and must be set to use the same magic cookie as the C node, `secretcookie`.

>     unix> cserver 3456

>     unix> erl -sname e1 -setcookie secretcookie
>     Erlang (BEAM) emulator version 4.9.1.2

>     Eshell V4.9.1.2  (abort with ^G)
>     (e1@idril)1> complex3:foo(3).
>     4
>     (e1@idril)2> complex3:bar(5).
>     10

> 4. Run the C node client example. Terminate `cserver` but not Erlang and start `cclient`. The Erlang node must be started before the C node client is.

>     unix> cclient

>     (e1@idril)3> complex3:foo(3).
>     4
>     (e1@idril)4> complex3:bar(5).
>     10

> 5. Run the C node server, long node names, example.

>     unix> cserver2 3456

>     unix> erl -name e1 -setcookie secretcookie
>     Erlang (BEAM) emulator version 4.9.1.2

>     Eshell V4.9.1.2  (abort with ^G)
>     (e1@idril.du.uab.ericsson.se)1> complex4:foo(3).
>     4
>     (e1@idril.du.uab.ericsson.se)2> complex4:bar(5).
>     10

Copyright © 1991-2003 Ericsson Utvecklings AB