PageRenderTime 74ms CodeModel.GetById 17ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 0ms

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