/Tools/NativeHost/Server.m

http://github.com/cacaodev/cappuccino · Objective C · 193 lines · 141 code · 35 blank · 17 comment · 21 complexity · 223cb8f618157fd24c214a74ebf6f9ec MD5 · raw file

  1. //
  2. // Server.m
  3. // NativeHost
  4. //
  5. // Created by Francisco Tolmasky on 8/18/09.
  6. // Copyright 2009 280 North, Inc.. All rights reserved.
  7. //
  8. #import "Server.h"
  9. #import "AppController.h"
  10. #import <sys/socket.h>
  11. #import <netinet/in.h>
  12. @implementation Server
  13. - (BOOL)start
  14. {
  15. NSBundle *mainBundle = [NSBundle mainBundle];
  16. NSString *serverPath = [mainBundle pathForResource:@"NativeHostServer" ofType:@"" inDirectory:@"Server"];
  17. if (!serverPath)
  18. {
  19. return YES;
  20. }
  21. // find an available port
  22. struct sockaddr_in addr;
  23. int sockfd;
  24. int attempts = 0;
  25. while (SERVER_PORT == 9191 && attempts++ < 5)
  26. {
  27. // Create a socket
  28. sockfd = socket( AF_INET, SOCK_STREAM, 0 );
  29. // Setup its address structure
  30. bzero( &addr, sizeof(struct sockaddr_in));
  31. addr.sin_family = AF_INET;
  32. addr.sin_addr.s_addr = htonl( INADDR_ANY );
  33. addr.sin_port = htons( 0 );
  34. // Bind it to an address and port
  35. bind( sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr));
  36. // Set it listening for connections
  37. listen( sockfd, 5 );
  38. // Find out what port the socket was bound to
  39. socklen_t namelen = sizeof(struct sockaddr_in);
  40. getsockname( sockfd, (struct sockaddr *)&addr, &namelen );
  41. if (addr.sin_port <= 1024)
  42. addr.sin_port = 9191;
  43. else
  44. SERVER_PORT = addr.sin_port;
  45. shutdown(sockfd, 2);
  46. }
  47. if (SERVER_PASSWORD == nil)
  48. {
  49. CFUUIDRef uuidObj = CFUUIDCreate(nil);
  50. SERVER_PASSWORD = (NSString*)CFUUIDCreateString(nil, uuidObj);
  51. CFRelease(uuidObj);
  52. }
  53. if (SERVER_USER == nil)
  54. {
  55. CFUUIDRef uuidObj = CFUUIDCreate(nil);
  56. SERVER_USER = (NSString*)CFUUIDCreateString(nil, uuidObj);
  57. CFRelease(uuidObj);
  58. // usernames can't have "-" in them
  59. SERVER_USER = [[SERVER_USER autorelease] stringByReplacingOccurrencesOfString:@"-" withString:@""];
  60. }
  61. // setup environment variables
  62. NSDictionary *env = [[[NSProcessInfo processInfo] environment] mutableCopy];
  63. [env setValue:[mainBundle resourcePath] forKey:@"NATIVEHOST_RESOURCES"];
  64. [env setValue:[[NSNumber numberWithInt:SERVER_PORT] stringValue] forKey:@"NATIVEHOST_SERVER_PORT"];
  65. [env setValue:SERVER_USER forKey:@"NATIVEHOST_SERVER_USER"];
  66. [env setValue:SERVER_PASSWORD forKey:@"NATIVEHOST_SERVER_PASSWORD"];
  67. // create pipes for stdout and stderr
  68. NSPipe * outputPipe = [NSPipe pipe];
  69. outputFile = [outputPipe fileHandleForReading];
  70. NSPipe * errorPipe = [NSPipe pipe];
  71. errorFile = [errorPipe fileHandleForReading];
  72. // setup the process
  73. process = [[NSTask alloc] init];
  74. [process setLaunchPath:serverPath];
  75. [process setStandardOutput:outputPipe];
  76. [process setStandardError:errorPipe];
  77. [process setEnvironment:env];
  78. [env release];
  79. [[NSNotificationCenter defaultCenter] addObserver:self
  80. selector:@selector(serverDidTerminate:)
  81. name:NSTaskDidTerminateNotification
  82. object:process];
  83. [process launch];
  84. BOOL didStartup = NO;
  85. NSData * data = nil;
  86. while (!didStartup && (data = [outputFile availableData]) && [data length])
  87. {
  88. NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  89. if (string)
  90. {
  91. NHLog(@"SERVER-OUT", string);
  92. didStartup = [string rangeOfString:@"Jack is starting up"].location != NSNotFound;
  93. [string release];
  94. }
  95. }
  96. NSLog(@"Server has started.");
  97. [[NSNotificationCenter defaultCenter]
  98. addObserver:self
  99. selector:@selector(didReadStdOut:)
  100. name:NSFileHandleReadCompletionNotification
  101. object:outputFile];
  102. [[NSNotificationCenter defaultCenter]
  103. addObserver:self
  104. selector:@selector(didReadStdErr:)
  105. name:NSFileHandleReadCompletionNotification
  106. object:errorFile];
  107. [outputFile readInBackgroundAndNotify];
  108. [errorFile readInBackgroundAndNotify];
  109. return didStartup;
  110. }
  111. - (void)stop
  112. {
  113. [process terminate];
  114. }
  115. - (void)didReadStdOut:(NSNotification *)aNotification
  116. {
  117. NSString * string = [[NSString alloc] initWithData:[[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem]
  118. encoding:NSASCIIStringEncoding];
  119. NHLog(@"SERVER-OUT", string);
  120. [string release];
  121. [outputFile readInBackgroundAndNotify];
  122. }
  123. - (void)didReadStdErr:(NSNotification *)aNotification
  124. {
  125. NSString * string = [[NSString alloc] initWithData:[[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem]
  126. encoding:NSASCIIStringEncoding];
  127. NHLog(@"SERVER-ERR", string);
  128. [string release];
  129. [errorFile readInBackgroundAndNotify];
  130. }
  131. - (void)serverDidTerminate:(NSNotification *)note
  132. {
  133. if ([process terminationStatus] != 0)
  134. {
  135. NSString *appName = [[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:@"CFBundleDisplayName"];
  136. if (appName == nil)
  137. appName = [[NSProcessInfo processInfo] processName];
  138. if (SERVER_PORT != 9191 && [self start])
  139. {
  140. NSRunAlertPanel(@"Unexpected Error",
  141. [NSString stringWithFormat:@"%@ was not able to complete the last action you performed. Try again, and report the problem if it continues to occur.", appName],
  142. @"OK", nil, nil);
  143. }
  144. else
  145. {
  146. NSRunAlertPanel(@"Unexpected Error",
  147. [NSString stringWithFormat:@"A fatal error occurred. %@ could not recover and must terminate.", appName],
  148. @"Terminate", nil, nil);
  149. exit(1);
  150. }
  151. }
  152. }
  153. - (void)dealloc
  154. {
  155. [process release];
  156. [super dealloc];
  157. }
  158. @end