From 2ded12a09d33fcf5aefe813a7da57298c60b39b5 Mon Sep 17 00:00:00 2001 From: Matthias Kruk Date: Sat, 1 Oct 2022 21:47:33 +0900 Subject: [PATCH] utils: Implement hooks in ipc-tap and ipc-inject To correctly forward foundry messages using ipc-tap and ipc-inject, it is necessary to move data that is usually not transferred over IPC messages. In order to transfer this data over ipc-tap and ipc-inject, hooks are necessary that allow user-data to be generated on the ipc-tap side and consumed on the ipc-inject side. This commit implements a simple mechanism that allows the user to specify hooks that are called by ipc-tap whenever a message on a user-defined topic is encountered. The hook can then inject messages into ipc-tap's data stream. On the ipc-inject side, the user may define hooks that are called when a user-message with a application-defined tag is encountered, allowing the injected message to be handled on the ipc-inject side. --- utils/ipc-inject.sh | 115 ++++++++++++++++++++++++++++++++++++++++---- utils/ipc-tap.sh | 72 +++++++++++++++++++++++++-- 2 files changed, 173 insertions(+), 14 deletions(-) diff --git a/utils/ipc-inject.sh b/utils/ipc-inject.sh index b0f418d..bb0000a 100755 --- a/utils/ipc-inject.sh +++ b/utils/ipc-inject.sh @@ -16,17 +16,121 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_hook() { + # local name="$1" # not used + local value="$2" + + local tag + local handler + + tag="${value%%:*}" + handler="${value#*:}" + + hooks["$tag"]="$handler" + return 0 +} + signal_handler() { signal_received=1 } +handle_hook_data() { + local endpoint="$1" + local hook_data="$2" + + local tag + local data + local handler + + if ! tag=$(json_object_get "$hook_data" "tag"); then + log_warn "Dropping message without tag" + return 1 + fi + + handler="${hooks[$tag]}" + + if [[ -z "$handler" ]]; then + log_warn "No handler for tag $tag" + return 0 + fi + + if ! data=$(json_object_get "$hook_data" "data"); then + log_warn "Dropping message without data" + return 1 + fi + + if ! "$handler" <<< "$data"; then + return 1 + fi + + return 0 +} + +handle_ipc_message() { + local endpoint="$1" + local data="$2" + + local topic + + if ! topic=$(ipc_msg_get_topic "$data"); then + log_warn "Dropping message without topic" + return 1 + fi + + if ! ipc_endpoint_publish "$endpoint" "$topic" "$data"; then + log_error "Could not publish message for $topic on $endpoint" + return 1 + fi + + return 0 +} + +handle_message() { + local endpoint="$1" + local message="$2" + + local decoded + local type + local data + local handler + + if ! decoded=$(base64 -d <<< "$message"); then + log_warn "Could not decode message" + return 1 + fi + + if ! type=$(json_object_get "$decoded" "type") || + ! data=$(json_object_get "$decoded" "data"); then + log_warn "Could not parse message" + return 1 + fi + + handler="${message_handler[$type]}" + + if [[ -n "$handler" ]]; then + "$handler" "$endpoint" "$data" + fi + + return 0 +} + main() { local endpoint local message + declare -gA hooks declare -gi signal_received + declare -gA message_handler + + message_handler["HookData"]=handle_hook_data + message_handler["IPCMessage"]=handle_ipc_message signal_received=0 + opt_add_arg "k" "hook" "v" "" \ + "Command for handling hook data" \ + '^[^:]+:.*$' \ + add_hook + if ! opt_parse "$@"; then return 1 fi @@ -39,16 +143,7 @@ main() { trap signal_handler INT TERM ABRT ALRM USR1 USR2 while (( signal_received == 0 )) && read -r message; do - local topic - - if ! topic=$(ipc_msg_get_topic "$message"); then - log_warn "Dropping message without topic" - continue - fi - - if ! ipc_endpoint_publish "$endpoint" "$topic" "$message"; then - log_error "Could not publish message for $topic on $endpoint" - fi + handle_message "$endpoint" "$message" done log_info "Closing endpoint $endpoint" diff --git a/utils/ipc-tap.sh b/utils/ipc-tap.sh index 45279b0..df49a13 100755 --- a/utils/ipc-tap.sh +++ b/utils/ipc-tap.sh @@ -25,10 +25,57 @@ add_topic() { return 0 } +add_hook() { + # local name="$1" # not needed + local value="$2" + + local topic + local hook + + topic="${value%%:*}" + hook="${value#*:}" + + # hooks is inherited from main() via opt_parse() + hooks["$topic"]="$hook" +} + signal_handler() { signal_received=1 } +output_message() { + local type="$1" + local data="$2" + + local message + + if ! message=$(json_object "type" "$type" \ + "data" "$data"); then + return 1 + fi + + if ! base64 -w 0 <<< "$message"; then + return 1 + fi + + printf '\n' + return 0 +} + +invoke_hooks() { + local topic="$1" + local message="$2" + + local data + + if data=$("${hooks[$topic]}" <<< "$message") && + [[ -n "$data" ]]; then + output_message "HookData" "$data" + fi + + return 0 +} + tap_topics() { local topics=("$@") @@ -62,10 +109,19 @@ tap_topics() { while (( signal_received == 0 )); do local message + local topic - if message=$(ipc_endpoint_recv "$endpoint" 5); then - printf '%s\n' "$message" + if ! message=$(ipc_endpoint_recv "$endpoint" 5); then + continue fi + + if ! topic=$(ipc_msg_get_topic "$message"); then + log_warn "Dropping message without topic" + continue + fi + + invoke_hooks "$topic" "$message" + output_message "IPCMessage" "$message" done fi @@ -77,10 +133,18 @@ tap_topics() { main() { local topics + declare -gA hooks topics=() - opt_add_arg "t" "topic" "rv" "" "A topic to tap into" "" add_topic + opt_add_arg "t" "topic" "rv" "" \ + "A topic to tap into" \ + '' \ + add_topic + opt_add_arg "k" "hook" "v" "" \ + "Hook to execute upon receipt" \ + '^[^:]+:.+$' \ + add_hook if ! opt_parse "$@"; then return 1 @@ -98,7 +162,7 @@ main() { exit 1 fi - if ! include "log" "opt" "ipc"; then + if ! include "log" "opt" "ipc" "json"; then exit 1 fi -- 2.47.3