2014-03-25 105 views
0

我想寫一個應用程序,將以編程方式登錄到遠程設備使用SSH很像一個期望腳本(我知道我可以使用期望但我想在Obj-c )。用於SSH的NSTask使用PTY

我已經研究了很多這方面的知識,我需要使用一個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:@[@"[email protected]"]]; 

    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; 
} 

回答

1

據我所知,NSTask不支持pty能力。使用諸如ssh之類的東西需要交互式pty設備上下文。

執行此操作的最簡單方法是forkpty,但此叉自身處理,因此它不能與NSTask一起使用。

最後我寫了一個包裝類,管理forkpty。這會生成子進程並調用forkptyexecve

這裏是我的實現:https://github.com/eonil/PseudoTeletypewriter.Swift

可以讀/使用單主設備文件句柄寫。 我確認sudo正在工作,我相信ssh也應該正常工作。

+0

是否有可能爲Swift 3.0更新它,因爲它應該是穩定的,未來的版本將向後兼容? – Arqu