【问题标题】:NSTask for SSH using PTY使用 PTY 进行 SSH 的 NSTask
【发布时间】:2023-03-27 07:25:02
【问题描述】:

我正在尝试编写一个应用程序,该应用程序将使用 SSH 以编程方式登录到远程设备,就像期望脚本一样(我知道我可以使用期望,但我想在 Obj-c 中执行此操作)。

我对此进行了很多研究,并且知道我需要使用 pty.我的代码适用于 telnet,但我似乎无法让 ssh 工作。似乎 SSH 没有使用 pty 来询问密码。当我执行以下代码时,我看到设备要求输入密码,但我没有看到我的 NSLog 输出。

我对此很陌生,可能已经不知所措,但我非常感谢任何可以帮助我完成这项工作的人。

#import <Foundation/Foundation.h>
#import <util.h>

@interface NSTask (PTY)

- (NSFileHandle *)masterSideOfPTYOrError:(NSError **)error;

@end

@implementation NSTask (PTY)

- (NSFileHandle *)masterSideOfPTYOrError:(NSError *__autoreleasing *)error {
int fdMaster, fdSlave;
int rc = openpty(&fdMaster, &fdSlave, NULL, NULL, NULL);
if (rc != 0) {
    if (error) {
        *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
    }
    return NULL;
}
fcntl(fdMaster, F_SETFD, FD_CLOEXEC);
fcntl(fdSlave, F_SETFD, FD_CLOEXEC);
NSFileHandle *masterHandle = [[NSFileHandle alloc] initWithFileDescriptor:fdMaster closeOnDealloc:YES];
NSFileHandle *slaveHandle = [[NSFileHandle alloc] initWithFileDescriptor:fdSlave closeOnDealloc:YES];
self.standardInput = slaveHandle;
self.standardOutput = slaveHandle;
return masterHandle;
}

@end

int main(int argc, const char * argv[])
{

@autoreleasepool {

    NSTask *task = [[NSTask alloc] init];

    [task setLaunchPath:@"/usr/bin/ssh"];

    [task setArguments:@[@"user@192.168.1.1"]];

    NSError *error;
    NSFileHandle *masterHandle = [task masterSideOfPTYOrError:&error];
    if (!masterHandle) {
        NSLog(@"error: could not set up PTY for task: %@", error);
        exit(0);
    }

    [task launch];

    [masterHandle waitForDataInBackgroundAndNotify];
    NSMutableString *buff = [[NSMutableString alloc] init];
    [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
                                                      object:masterHandle queue:nil
                                                  usingBlock:^(NSNotification *note)
     {
         NSData *outData = [masterHandle availableData];
         NSString *outStr = [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
         [buff appendString:outStr];
         NSLog(@"output: %@", outStr);

         NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"sername:"
                                                                                options:NSRegularExpressionCaseInsensitive
                                                                                  error:nil];
         NSTextCheckingResult *match = [regex firstMatchInString:buff
                                                         options:0
                                                           range:NSMakeRange(0, [buff length])];
         if (match) {
             NSLog(@"got a match!!");
             [buff setString:@""];
             [masterHandle writeData:[@"bhughes\n" dataUsingEncoding:NSUTF8StringEncoding]];
         }

         NSLog(@"Exiting function.\n");
         [masterHandle waitForDataInBackgroundAndNotify];
     }];

    [task waitUntilExit];

    NSLog(@"Program complete.\n");
}
return 0;
}

【问题讨论】:

    标签: objective-c ssh nstask pty


    【解决方案1】:

    据我所知,NSTask 不支持pty 能力。使用 ssh 之类的东西需要交互式 pty 设备上下文。

    最简单的方法是forkpty,但是这个forks进程本身,所以它不能和NSTask一起使用。

    最后我写了一个包装类来管理forkpty。这会派生一个子进程并调用forkptyexecve

    这是我的实现:https://github.com/eonil/PseudoTeletypewriter.Swift

    您可以使用单个主设备文件句柄进行读/写。 我确认sudo 可以正常工作,我相信ssh 也应该可以正常工作。

    【讨论】:

    • 是否可以为 Swift 3.0 更新它,因为它应该是稳定的并且未来的版本将向后兼容?
    猜你喜欢
    • 2012-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-21
    • 1970-01-01
    • 2019-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多