Changeset 112
- Timestamp:
- 08/20/07 04:11:47 (1 year ago)
- Files:
-
- branches/eric/Libs/SSHAgent.h (modified) (2 diffs)
- branches/eric/Libs/SSHAgent.m (modified) (22 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/eric/Libs/SSHAgent.h
r93 r112 9 9 NSString *agentSocketPath; 10 10 NSArray *keysOnAgent; 11 NSTask *agentTask; 11 12 12 13 NSLock *agentLock; … … 31 32 - (void)handleAgentConnections; 32 33 - (void)inputFromClient:(id)object; 33 - (void) checkAgent;34 - (void)agentFailed:(NSNotification *)notification; 34 35 35 36 - (NSArray *)currentKeysOnAgent; branches/eric/Libs/SSHAgent.m
r93 r112 114 114 - (BOOL)isRunning 115 115 { 116 return [ self PID] > 0;116 return [agentTask isRunning]; 117 117 } 118 118 … … 153 153 } 154 154 155 156 /* eric - 20070819 - Tried several variations before settling on this. It gives us notification 157 of task termination and unfortunatly NSPipe is very bad about dealing with libc apps that don't 158 flush at the end of every line. The sleep is a hack, but it works and will only happen one */ 159 155 160 /* Start the agent. */ 156 161 - (BOOL)start 157 162 { 158 NSString *line;159 160 163 if ([self isRunning]) 161 164 { … … 165 168 166 169 [self setAgentSocketPath:nil]; 167 168 if (![self socketPath]) 169 { 170 NSLog(@"DEBUG: start: socketPath not set"); 170 [self setPID:-1]; 171 172 /* Create temporary path for ssh-agent */ 173 char template[] = "/tmp/ssh-XXXXXXXXXX/agent.XXXXX"; 174 char *retVal = mktemp(template); 175 if ( (long)retVal == -1 ) { 176 NSLog(@"SSHAgent start: temp path could not be generated."); 171 177 return NO; 172 178 } 173 174 /* Initialize a ssh-agent SSHTool, set the arguments to -c for c-shell output. */ 175 SSHTool *theTool = [SSHTool toolWithName:@"ssh-agent"]; 176 [theTool setArgument:@"-c"]; 177 178 /* Launch the agent and retrieve stdout. */ 179 NSString *theOutput = [theTool launchForStandardOutput]; 180 if (!theOutput) 181 { 182 NSLog(@"ssh-agent didn't launch"); 183 return NO; 184 } 185 186 /* Split the lines with delimiter ";\n". */ 187 NSArray *lines = [theOutput componentsSeparatedByString:@";\n"]; 188 NSEnumerator *e = [lines objectEnumerator]; 189 while (line = [e nextObject]) 190 { 191 /* Split the line with delimiter " ". */ 192 NSArray *columns = [line componentsSeparatedByString:@" "]; 193 if ([columns count] != 3) 194 continue; 195 196 NSString *key = [columns objectAtIndex:1]; 197 /* If 2nd column matches "SSH_AUTH_SOCK", then 3rd column is the socket path. */ 198 if ([key isEqualToString:@"SSH_AUTH_SOCK"]) 199 [self setAgentSocketPath:[columns objectAtIndex:2]]; 200 201 /* If 2nd column matches "SSH_AGENT_PID", then 3rd column is the PID. */ 202 else if ([key isEqualToString:@"SSH_AGENT_PID"]) 203 [self setPID:[[columns objectAtIndex:2] intValue]]; 204 } 179 NSString *tempPath = [NSString stringWithCString:retVal]; 180 181 /* Setup the agentTask and launch */ 182 agentTask = [[[NSTask alloc] init] retain]; 183 [agentTask setLaunchPath:@"/usr/bin/ssh-agent"]; 184 [agentTask setArguments:[NSArray arrayWithObjects:@"-c",@"-d",@"-a", tempPath, nil]]; 185 [agentTask launch]; 186 187 /* set paths and PID's... we already know them in advance */ 188 [self setAgentSocketPath: tempPath]; 189 [self setPID:[agentTask processIdentifier]]; 205 190 206 191 /* If the agent is not running, or the socket path is empty then stop the agent and fail */ … … 211 196 return NO; 212 197 } 198 199 /* We need to give ssh-agent time to startup, it's an ugly hack but NSPipes wen't cutting it */ 200 sleep(1); 213 201 214 202 /* Handle connections in a seperate thread. */ 215 203 [NSThread detachNewThreadSelector:@selector(handleAgentConnections) toTarget:self withObject:nil]; 216 217 /* Check if agent is alive in a seperate thread. */ 218 [NSThread detachNewThreadSelector:@selector(checkAgent) toTarget:self withObject:nil]; 204 205 /* Watch for terminaton of the agent */ 206 [[NSNotificationCenter defaultCenter] addObserver:self 207 selector:@selector(agentFailed:) 208 name:NSTaskDidTerminateNotification 209 object:agentTask]; 210 219 211 [[NSNotificationCenter defaultCenter] postNotificationName:@"AgentStarted" object:nil]; 220 212 … … 230 222 231 223 /* We don't need to check if this fails. We clean up the variables either way. */ 224 [agentTask terminate]; 232 225 kill([self PID], SIGTERM); 233 226 … … 253 246 - (void)handleAgentConnections 254 247 { 255 NS AutoreleasePool *pool = [[NSAutoreleasePool alloc] init];248 NSTask *currentAgent = agentTask; 256 249 257 250 /* Fill the sockaddr_un structs. */ … … 271 264 NSLog(@"handleAgentConnections: socket() failed"); 272 265 [self stop]; 273 [pool release];274 266 return; 275 267 } … … 283 275 NSLog(@"handleAgentConnections: bind() failed"); 284 276 [self stop]; 285 [pool release];286 277 return; 287 278 } … … 293 284 NSLog(@"handleAgentConnections: listen() failed"); 294 285 [self stop]; 295 [pool release];296 286 return; 297 287 } … … 305 295 NSLog(@"handleAgentConnections: malloc() failed"); 306 296 [self stop]; 307 [pool release];308 297 return; 309 298 } … … 322 311 while ((result = select(largestFileDescriptor + 1, &readFileDescriptors, NULL, NULL, NULL))) 323 312 { 313 if (agentTask != currentAgent) { 314 NSLog(@"handleAgentConnections: Hmmm our agent has gone away, close shop and hope the next one is up and running"); 315 [self stop]; 316 free(allFileDescriptors); 317 return; 318 } 324 319 if (result == -1 && errno == EINTR) 325 320 continue; … … 331 326 [self stop]; 332 327 free(allFileDescriptors); 333 [pool release];334 328 return; 335 329 } … … 349 343 NSLog(@"handleAgentConnections: realloc() failed"); 350 344 [self stop]; 351 [pool release];352 345 return; 353 346 } … … 366 359 [self stop]; 367 360 free(allFileDescriptors); 368 [pool release];369 361 return; 370 362 } … … 379 371 [self stop]; 380 372 free(allFileDescriptors); 381 [pool release];382 373 return; 383 374 } … … 475 466 476 467 free(allFileDescriptors); 477 [pool release];478 468 } 479 469 … … 481 471 - (void)inputFromClient:(id)object 482 472 { 483 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];484 485 473 int destinationFileDescriptor = [[object objectAtIndex:0] intValue]; 486 474 const char *readBuffer = [[object objectAtIndex:1] cString]; … … 516 504 write(sourceFileDescriptor, "\0\0\0\5\2\0\0\0\0", 9); 517 505 518 [pool release];519 506 return; 520 507 } … … 525 512 write(sourceFileDescriptor, "\0\0\0\5\f\0\0\0\0", 9); 526 513 527 [pool release];528 514 return; 529 515 } … … 540 526 /* Write the buffer to the agent. */ 541 527 write(destinationFileDescriptor, readBuffer, len); 542 [pool release]; 543 } 544 545 /* This method is called in a separate thread. It periodically checks if the ssh-agent is still alive. */ 546 - (void)checkAgent 547 { 548 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 549 int currentPID = [self PID]; 550 while (getpgid(currentPID) != -1) 551 { 552 /* The agent is still alive, so sleep for a while before checking again */ 553 sleep(30); 554 555 /* If the PID has changed while we were sleeping then the agent has been stopped and restarted. 556 In this instance a new thread would have been spawned to monitor the new agent, and the agent 557 we were monitoring will no longer exist. Exit early to avoid notifying the user that the old 558 agent is gone */ 559 if (currentPID != [self PID]) 560 { 561 [pool release]; 562 return; 563 } 564 } 565 566 [self stop]; 567 528 } 529 530 /* this method is called from a NSTask notification when the agent dies out from underneath us */ 531 - (void)agentFailed:(NSNotification *)notification 532 { 533 /* do nothing if the notification is not for out current agent */ 534 if ( [notification object] != agentTask ) { 535 return; 536 } 537 568 538 /* Dictionary for the panel. */ 569 539 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; … … 581 551 SInt32 error; 582 552 CFOptionFlags response; 583 CFUserNotificationRef notification = CFUserNotificationCreate(nil, 30, CFUserNotificationSecureTextField(0), &error, (CFDictionaryRef)dict);553 CFUserNotificationRef notificationRef = CFUserNotificationCreate(nil, 30, CFUserNotificationSecureTextField(0), &error, (CFDictionaryRef)dict); 584 554 585 555 /* If we couldn't receive a response, return nil. */ 586 if (error || CFUserNotificationReceiveResponse(notification, 0, &response)) 587 { 588 [pool release]; 556 if (error || CFUserNotificationReceiveResponse(notificationRef, 0, &response)) 557 { 589 558 return; 590 559 } … … 593 562 if ((response & 0x3) == kCFUserNotificationDefaultResponse) 594 563 [self start]; 595 596 [pool release];597 564 } 598 565
