Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add connection prehook for scripted node traversal #432

Merged
merged 10 commits into from
Feb 18, 2024

Conversation

martinhpedersen
Copy link
Member

@martinhpedersen martinhpedersen commented Nov 23, 2023

Adds low-level connection prehook for all transports, implemented by spawning a user-defined process communicating with the remote station over stdio.

This enables scripted node traversal (#114) at the lowest possible level. To make it more practical and user-friendly, we need to add a layer on top. Not everyone is comfortable writing bash scripts/programs able to do this, so we need a simplified language with just enough flexibility to express the command/response sequences commonly used in packet node traversal.


TODO

  • Write documentation
    • New connect URI query parameters (wiki + pat connect help)
    • Describe the protocol between Pat and the spawned process (wiki)

For use when spawning a child process, i.e. the session prehook for
scripted packet node traversal.

Ref #114
Implemented by spawning a user-defined process communicating with the
remote station over stdio.

Issue #114
Probably not required anyway :)
@martinhpedersen martinhpedersen added this to the v0.16.0 milestone Nov 23, 2023
@martinhpedersen martinhpedersen self-assigned this Nov 23, 2023
conn_prehook.go Outdated Show resolved Hide resolved
conn_prehook.go Outdated Show resolved Hide resolved
@martinhpedersen
Copy link
Member Author

Example based on the initial feature request.

Script: my-prehook.sh

#!/bin/sh
set -e
function wait {
	local timeout=$1 keyword=$2 line=""
	while [[ $line != $keyword ]]; do read -t $timeout -r line; done
}
function print {
	shift
	echo "${@}\r"
}

#########################################
# The node traversal script starts here #
#########################################
print C K7LL-7
wait 10 Connected
print C K7LL-10
wait 10 Connected

Usage:

pat connect 'ax25:///K7LL-5?prehook=my-prehook.sh'

@martinhpedersen
Copy link
Member Author

... We should probably define a location for these scripts and append that to the spawned process' PATH.

@martinhpedersen martinhpedersen marked this pull request as ready for review December 9, 2023 15:49
func Verify(file string) error { _, err := lookPath(file); return err }

func lookPath(file string) (string, error) {
// Look in our custom location first
Copy link
Contributor

@xylo04 xylo04 Dec 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, but in an earlier comment you described trying to append the prehooks dir to the process' PATH. If that worked the way I assumed, our prehooks dir would just be the first place LookPath would explore, and you wouldn't have to do two checks. I'm guessing that didn't work out?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would be much easier. I thought the issue was that Go's standard library didn't provide a portable way of modifying $PATH. The syntax differs between Windows and linux/darwin.

.. However, while writing this comment I discovered os.PathListSeparator 😄 I'll give it another try! Thanks 🙂

log.Println("Running prehook...")
script := prehook.Script{
File: exec,
Args: url.Params["prehook-arg"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose I could just try this, but I can also ask you about your intent: url.Params["prehook-arg"] gives back an array of args. If I have multiple args I need to pass to the script, do I make multiple URL params with the same key?

E.g. if I want to invoke the prehook script with:

my-prehook.sh --bool-arg --arg2=value

then I encode that in the URL as

ax25:///K7LL-5?prehook=my-prehook.sh&prehook-arg=--bool-arg&prehook-arg=--arg2=value

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct 🙂 As an alternative we could consider a single argument with a separator. However, then we would also need to implement escaping in case the separator should be passed to the script.

@@ -33,6 +33,12 @@ path:
params:
?freq= Sets QSY frequency (ardop and ax25 only)
?host= Overrides the host part of the path. Useful for serial-tnc to specify e.g. /dev/ttyS0.
?prehook= Sets an executable middleware to run before the connection is handed over to the B2F protocol.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-level question: since we are making a connection pre-hook for initializing connections, does that now require that there's a post-hook for tearing the connection down cleanly? Maybe the proposed use case, AX.25 packet, is smart enough to tear down traversal links.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a very good question! 🤔

I would imagine that all connections would be teared down once one of the links are disconnected. Otherwise, you'd run the risk of dangling connections in case the dialer looses it's connection. But this is definitely something to investigate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could check to see if Winlink Express provides such a feature. If they don't, I would imagine it's not very common 😁

if err != nil {
return err
}
cmd := exec.CommandContext(ctx, execPath, script.Args...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the pre-hook script communicate its progress? Is exit code 0 the main/only way? I could see eventually expanding it to where the connection script can signal that the connection is ready, then Pat can signal when B2F is done and let the script tear down the connection. But I don't think that's necessary for v1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exit code 0 == success. Any other exit code is assumed to be a failure, and the connection is closed with an error presented to the user.

I see your point. We could maybe use a signal like SIGCONT for this, to keep stdio as transparent as possible. We would also need another mechanism to communicate that the connection is ready for B2F, as this is currently implemented by the child process exiting. But yes, maybe not necessary for v1.

@martinhpedersen martinhpedersen merged commit 9fe80af into develop Feb 18, 2024
4 checks passed
@martinhpedersen martinhpedersen deleted the feature/conn-prehook branch February 18, 2024 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants