-
Notifications
You must be signed in to change notification settings - Fork 0
SSH module #1
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
base: main
Are you sure you want to change the base?
SSH module #1
Changes from all commits
f19b870
891e802
362deb9
0bcbc9e
a183d4a
ab8f27f
e41fd92
dcf7423
956d1d1
954cf91
4297dc3
d160abb
27b2258
8a135ef
830344f
4905b47
7d6b10c
95a2701
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| .acton.lock | ||
| .build | ||
| out | ||
| zig-cache | ||
| zig-out |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,32 +1,45 @@ | ||||||||||||||||||||||||||||||||||||
| import net | ||||||||||||||||||||||||||||||||||||
| import testing | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def version() -> str: | ||||||||||||||||||||||||||||||||||||
| """Get the libssh version""" | ||||||||||||||||||||||||||||||||||||
| """Get the acton-ssh version""" | ||||||||||||||||||||||||||||||||||||
| NotImplemented | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def _test_version(): | ||||||||||||||||||||||||||||||||||||
| testing.assertEqual("0.11.0", version()) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| actor Client(cap: net.TCPConnectCap, | ||||||||||||||||||||||||||||||||||||
| host: str, | ||||||||||||||||||||||||||||||||||||
| username: str, | ||||||||||||||||||||||||||||||||||||
| on_connect: action(Client) -> None, | ||||||||||||||||||||||||||||||||||||
| on_close: action(Client, str) -> None, | ||||||||||||||||||||||||||||||||||||
| key: ?str=None, | ||||||||||||||||||||||||||||||||||||
| password: ?str=None, | ||||||||||||||||||||||||||||||||||||
| password: str, | ||||||||||||||||||||||||||||||||||||
| port: u16=22, | ||||||||||||||||||||||||||||||||||||
| subsystem: str | ||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||
| """SSH Client""" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| proc def _pin_affinity() -> None: | ||||||||||||||||||||||||||||||||||||
| NotImplemented | ||||||||||||||||||||||||||||||||||||
| _pin_affinity() | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # haha, this is really a pointer :P | ||||||||||||||||||||||||||||||||||||
| var _ssh_session: u64 = 0 | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def get_ssh_session(): | ||||||||||||||||||||||||||||||||||||
| return _ssh_session | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def get_password() -> str: | ||||||||||||||||||||||||||||||||||||
| return password | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def get_subsystem() -> str: | ||||||||||||||||||||||||||||||||||||
| return subsystem | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| proc def _init() -> None: | ||||||||||||||||||||||||||||||||||||
| """Initialize the SSH client""" | ||||||||||||||||||||||||||||||||||||
| NotImplemented | ||||||||||||||||||||||||||||||||||||
| _init() | ||||||||||||||||||||||||||||||||||||
| print("SSH Client connected") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # print("SSH Client connected") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # action def close(on_close: action(TLSConnection) -> None) -> None: | ||||||||||||||||||||||||||||||||||||
| # """Close the connection""" | ||||||||||||||||||||||||||||||||||||
|
|
@@ -37,33 +50,81 @@ actor Client(cap: net.TCPConnectCap, | |||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||
| # def _connect(c): | ||||||||||||||||||||||||||||||||||||
| # NotImplemented | ||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| proc def disconnect() -> None: | ||||||||||||||||||||||||||||||||||||
| """Disconnect the SSH client""" | ||||||||||||||||||||||||||||||||||||
| NotImplemented | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # TODO: implement support for channels | ||||||||||||||||||||||||||||||||||||
| # AFAIK, all things over ssh are done via channels, so need some channel | ||||||||||||||||||||||||||||||||||||
| # primitive, maybe an actor per channel that then multiplexes into the Client | ||||||||||||||||||||||||||||||||||||
| # session? Prolly need some higher level wrappers for common things like | ||||||||||||||||||||||||||||||||||||
| # starting a shell or running a single command. SFTP / SCP would be nice too, | ||||||||||||||||||||||||||||||||||||
| # but for sometime in the future. Custom subsystems need to be supported too. | ||||||||||||||||||||||||||||||||||||
| actor Channel(client: Client): | ||||||||||||||||||||||||||||||||||||
| """SSH Channel""" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| var _ssh_channel: u64 = 0 | ||||||||||||||||||||||||||||||||||||
| var _ssh_session: u64 = client.get_ssh_session() | ||||||||||||||||||||||||||||||||||||
| var _password: str = client.get_password() | ||||||||||||||||||||||||||||||||||||
| var _subsystem: str = client.get_subsystem() | ||||||||||||||||||||||||||||||||||||
| var payload = None | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| proc def _pin_affinity() -> None: | ||||||||||||||||||||||||||||||||||||
| NotImplemented | ||||||||||||||||||||||||||||||||||||
| _pin_affinity() | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| proc def _init() -> None: | ||||||||||||||||||||||||||||||||||||
| """Initialize the SSH Channel""" | ||||||||||||||||||||||||||||||||||||
| NotImplemented | ||||||||||||||||||||||||||||||||||||
| _init() | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+78
to
+81
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
the channel actor needs to be running on the same worker thread as the Client, since it accesses the same socket and accessing the socket in our libuv IO loop means we need to be running on the same worker therad. libuv is not thread safe, so accessing the IO loop of another worker thread will cause crashes. It might be working for you while developing today, but it's because there is so little going on. If we write a test with 1000 concurrently running SSH clients, it would mostly likely segfault. Actor continuations are run by worker threads. Per default they go to the global shared readyQ and then a "random" worker thread picks it up from there. The actor initialization (running the code body of the actor) always runs in a random worker, so the first thing it should do is set affinity, then any subsequent continuations will run in the correct worker thread. Thus, we must NOT run the ssh init stuff too early, that's why I'm suggesting here to first set affinity then do There are some affinity related functions in rts.h or around there. See the other IO actors that interact with it. We can read out our affinity by reading the $Actor->affinity attribute (or whatever it is called). Let me know if you have any questions and we can walk through it! :) |
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # print("SSH Channel created") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def setPayload(p: str): | ||||||||||||||||||||||||||||||||||||
| payload = p | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def getPayload(): | ||||||||||||||||||||||||||||||||||||
| return payload | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def sendNCPayload() -> str: | ||||||||||||||||||||||||||||||||||||
| """Send payload""" | ||||||||||||||||||||||||||||||||||||
| NotImplemented | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| actor main(env): | ||||||||||||||||||||||||||||||||||||
| def on_connect(client: Client): | ||||||||||||||||||||||||||||||||||||
| print("Connected") | ||||||||||||||||||||||||||||||||||||
| # print("Client connected") | ||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def on_close(client: Client, error: str): | ||||||||||||||||||||||||||||||||||||
| print("Error", error) | ||||||||||||||||||||||||||||||||||||
| # print("Connection closed", error) | ||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| print(version()) | ||||||||||||||||||||||||||||||||||||
| c = Client( | ||||||||||||||||||||||||||||||||||||
| net.TCPConnectCap(net.TCPCap(net.NetCap(env.cap))), | ||||||||||||||||||||||||||||||||||||
| "localhost", | ||||||||||||||||||||||||||||||||||||
| "foo", | ||||||||||||||||||||||||||||||||||||
| on_connect, | ||||||||||||||||||||||||||||||||||||
| on_close, | ||||||||||||||||||||||||||||||||||||
| password="bar", | ||||||||||||||||||||||||||||||||||||
| port=2223, | ||||||||||||||||||||||||||||||||||||
| port=830, | ||||||||||||||||||||||||||||||||||||
| subsystem="netconf", | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| cc = Channel(c) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # get netconf server's capabilities | ||||||||||||||||||||||||||||||||||||
| cc.setPayload('<?xml version="1.0"?><rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"message-id="101"><get-config><source><running/></source></get-config></rpc>]]>]]>') | ||||||||||||||||||||||||||||||||||||
| print("\n\npayload 1:\n", cc.getPayload(), "\n\npayload 1 end") | ||||||||||||||||||||||||||||||||||||
| print("\nNC response 1:\n", cc.sendNCPayload(), "\n\nNC response 1 end \n\n") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # TODO do this in disconnect(): gracefully close a channel | ||||||||||||||||||||||||||||||||||||
| cc.setPayload('<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><close-session/></rpc>]]>]]>') | ||||||||||||||||||||||||||||||||||||
| print("\n\npayload 2:\n", cc.getPayload(), "\n\npayload 2 end") | ||||||||||||||||||||||||||||||||||||
| print("\nNC response 2:\n", cc.sendNCPayload(), "\n\nNC response 2 end \n\n") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| c.disconnect() | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| env.exit(0) | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then need to implement the necessary C call to return the actual affinity of the Client actor. We need this as action so the Channel actor can query the Client about affinity, so the Channel actor in turn can make sure it gets pinned to the same worker thread as the Client.