From 7d2b8c969fd041fa5e6a33dc01939342cb906549 Mon Sep 17 00:00:00 2001 From: abelino Date: Sat, 31 Aug 2024 19:59:05 -0700 Subject: [PATCH] Add support for handling responses to gen_server calls --- lib/bacnet.ex | 7 ++++- src/ei_client.c | 13 ++++++++ src/ei_client.h | 1 + src/main.c | 83 ++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 96 insertions(+), 8 deletions(-) diff --git a/lib/bacnet.ex b/lib/bacnet.ex index 229c3c6..aa58e5d 100644 --- a/lib/bacnet.ex +++ b/lib/bacnet.ex @@ -12,7 +12,12 @@ defmodule Bacnet do GenServer.start_link(__MODULE__, nil, name: __MODULE__) end - @impl GenServer + @spec add_device(device :: term) :: :ok | {:error, term} + def add_device(device) do + GenServer.call({:global, :bacnetd}, {:add_device, device}) + end + + end @impl GenServer def init(_) do {:ok, nil} end diff --git a/src/ei_client.c b/src/ei_client.c index 77f01f3..da4ddc5 100644 --- a/src/ei_client.c +++ b/src/ei_client.c @@ -77,6 +77,19 @@ bool ei_client_send(char *process_name, ei_x_buff *msg) return ret == 0 ? true : false; } +bool ei_client_send_to(erlang_pid *pid, ei_x_buff *msg) +{ + if (!client.ready) { + return false; + } + + pthread_mutex_lock(&client.lock); + int ret = ei_send(client.fd, pid, msg->buff, msg->index); + pthread_mutex_unlock(&client.lock); + + return ret == 0 ? true : false; +} + bool ei_client_call(char *module, char *func, ei_x_buff *args, ei_x_buff *out) { pthread_mutex_lock(&client.lock); diff --git a/src/ei_client.h b/src/ei_client.h index 5cc7694..7d6318b 100644 --- a/src/ei_client.h +++ b/src/ei_client.h @@ -5,6 +5,7 @@ bool ei_client_config(const char *nodename, const char *cookie); bool ei_client_send(char *process_name, ei_x_buff *msg); +bool ei_client_send_to(erlang_pid *pid, ei_x_buff *msg); bool ei_client_call(char *module, char *func, ei_x_buff *msg, ei_x_buff *out); bool ei_client_recv(erlang_msg *meta, ei_x_buff *msg); diff --git a/src/main.c b/src/main.c index 874a799..741ecda 100644 --- a/src/main.c +++ b/src/main.c @@ -5,8 +5,12 @@ #include "ei_client.h" #include "ei_log.h" +#define STRINGIFY(x) #x +#define TOSTR(x) STRINGIFY(x) +#define ERL_TUPLE TOSTR(ERL_SMALL_TUPLE_EXT) TOSTR(ERL_SMALL_TUPLE_EXT) + static arg_t args; -static void unimplemented(const char *msg_type); +static void handle_call(char *buf, int *index, ei_x_buff *reply); int main(int argc, char **argv) { @@ -31,14 +35,14 @@ int main(int argc, char **argv) int index = 0; int version = 0; - int arity = 0; + ei_term t = { 0 }; char msg_type[MAXATOMLEN] = { 0 }; char *buf = msg.buff; bool bad_msg = (false || ei_decode_version(buf, &index, &version) - || ei_decode_tuple_header(buf, &index, &arity) - || (arity < 2) + || ei_decode_tuple_header(buf, &index, &t.size) + || (t.size < 2) || ei_decode_atom(buf, &index, msg_type)); if (bad_msg) { @@ -46,7 +50,44 @@ int main(int argc, char **argv) } if (strcmp(msg_type, "$gen_call") == 0) { - unimplemented("$gen_call"); + erlang_pid from_pid; + erlang_ref from_ref; + + // request {:"$gen_call", {PID, [:alias | REF]}, TUPLE}} + bool bad_msg = (false + || ei_decode_tuple_header(buf, &index, &t.size) + || (t.size != 2) + || ei_decode_pid(buf, &index, &from_pid) + || ei_decode_list_header(buf, &index, &t.size) + || (t.size != 1) + || ei_decode_atom(buf, &index, t.value.atom_name) + || strcmp(t.value.atom_name, "alias") + || ei_decode_ref(buf, &index, &from_ref) + || ei_get_type(buf, &index, (int *)&t.ei_type, &t.size) + || memchr(ERL_TUPLE, t.ei_type, sizeof(ERL_TUPLE)) == NULL); + + if (bad_msg) { + LOG_ERR("Failed decoding message"); + goto cleanup; + } + + // reply {[:alias | REF], REPLY} + ei_x_buff reply; + ei_x_new_with_version(&reply); + ei_x_encode_tuple_header(&reply, 2); + ei_x_encode_list_header(&reply, 1); + ei_x_encode_atom(&reply, "alias"); + ei_x_encode_ref(&reply, &from_ref); + + handle_call(buf, &index, &reply); + + if (!ei_client_send_to(&from_pid, &reply)) { + LOG_ERR("Unable to send reply"); + } + + ei_x_free(&reply); + } else { + LOG_WRN("bacnetd: unknown message type %s", msg_type); } cleanup: @@ -56,7 +97,35 @@ int main(int argc, char **argv) return 0; } -static void unimplemented(const char *msg_type) +static void add_device(char *buf, int *index, ei_x_buff *reply) +{ + ei_x_encode_tuple_header(reply, 2); + ei_x_encode_atom(reply, "error"); + ei_x_encode_atom(reply, "unimplemented"); +} + +static void handle_call(char *buf, int *index, ei_x_buff *reply) { - LOG_WRN("bacnetd: %s unimplemented", msg_type); + int size = 0; + char call_type[MAXATOMLEN] = { 0 }; + + bool bad_msg = (false + || ei_decode_tuple_header(buf, index, &size) + || ei_decode_atom(buf, index, call_type)); + + if (bad_msg) { + ei_x_encode_tuple_header(reply, 2); + ei_x_encode_atom(reply, "error"); + ei_x_encode_atom(reply, "bad_request"); + return; + } + + if (strcmp(call_type, "add_device") == 0) { + add_device(buf, index, reply); + } + else { + ei_x_encode_tuple_header(reply, 2); + ei_x_encode_atom(reply, "error"); + ei_x_encode_atom(reply, "unimplemented"); + } }