Odd behavior on iOS with NIOSSHClient

I am using the code in the example NIOSSHClient in my app, to issue commands to some servers. I am intending for the application to run on macOS as well as iOS. So far so good except for one small thing.

My app works fine on macOS but on iOS, every command sent via SSH returns an exit code of 1 and essentially doesn't work. I found that if I put a small sleep in the code then it works fine. I am running the iOS variant on the iPad simulator.

The code code below is from ExecHandler.swift in the channelActive() function. It is essentially unchanged from the sample app except for dup()ing the file handles and adding the aforementioned sleep() call.

    }.takingOwnershipOfDescriptors(input: dup(0), output: dup(1)).whenComplete { result in
        switch result {
        case .success:
            // We need to exec a thing.
            let execRequest = SSHChannelRequestEvent.ExecRequest(command: self.command, wantReply: false)
            context.triggerUserOutboundEvent(execRequest).whenFailure { _ in
                context.close(promise: nil)
            }
            Thread.sleep(forTimeInterval: 0.1)  // Hmm, without this the command fails if run from iOS.
        case .failure(let error):
            context.fireErrorCaught(error)
        }

Does anyone have any ideas on this?

The most likely outcome is that you're not waiting for the response. The sleep allows the response to be generated while you're forcing the sleep. When do you tear the full client down?

With very minor changes, the SSH code I am using was taken directly from the sample app of NIOSSHClient. I have the same wait()s on channels and promises. In my case the biggest change is not getting user input for credentials on the command line (i.e. InteractivePasswordPromptDelegate). Also the sample is command-line only, whereas I incorporated that code into a proper GUI application. Kinda smells like a race condition to me.

I have yet another "solution". I have no use for using stdout and stdin so I have now eliminated the GlueHandler and use of NIOPipeBootstrap, and replaced the contents of channelActive() of ChannelDuplexHandler with the following two lines:

let execRequest = SSHChannelRequestEvent.ExecRequest(command: self.command, wantReply: true)
context.triggerUserOutboundEvent(execRequest, promise: nil)

All appears to be working normally now. I can't help wonder if there's still a gotcha lurking somewhere in there. :slight_smile:

That should work just fine. The use of stdin may well have been your issue, actually, as it could potentially be triggering a close on the channel.

In trying to investigate the source of a Mac Book M4 Pro crash that made the trackpad unresponsive to clicks, and also killed the Dock and Application Switcher, I found multiple log entries on my Mac (running Sequoia 15.6.1) of the form “endpointsecurity [info][ExecHandler.swift@335] Executable file /usr/libexec/xpcproxy - hash found in cache!” and
”endpointsecurity [info][ExecHandler.swift@335] Executable file /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Support/mdworker_shared - hash found in cache!”
The only reference I can find to “ExecHandler.swift” in a web search is to this discussion. I apologize if my posting is off-target, but am hoping that someone could tell me if this is expected in the log. I presume “hash found in cache” from endpointsecurityd refers to the hash of a potentially malicious file. I have scanned what I believe are the relevant cache files and binaries with several antivirus engines and did not get any hits. I do have reason to suspect that I might have been hacked by some sophisticated and malign actors. Thanks very much for any advice/analysis!

This is unfortuntely unrelated to SwiftNIO, so we can’t help you with this issue. If you are concerned about the security status of your Mac, please use Apple’s existing reporting mechanisms, either Feedback Assistant, the Security or Privacy vulnerability reporting system, or asking for more assistance at the Developer Forums.