mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
194 lines
5.7 KiB
Objective-C
194 lines
5.7 KiB
Objective-C
//
|
|
// Server.m
|
|
// NativeHost
|
|
//
|
|
// Created by Francisco Tolmasky on 8/18/09.
|
|
// Copyright 2009 280 North, Inc.. All rights reserved.
|
|
//
|
|
|
|
#import "Server.h"
|
|
#import "AppController.h"
|
|
|
|
#import <sys/socket.h>
|
|
#import <netinet/in.h>
|
|
|
|
@implementation Server
|
|
|
|
- (BOOL)start
|
|
{
|
|
NSBundle *mainBundle = [NSBundle mainBundle];
|
|
NSString *serverPath = [mainBundle pathForResource:@"NativeHostServer" ofType:@"" inDirectory:@"Server"];
|
|
|
|
if (!serverPath)
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
// find an available port
|
|
struct sockaddr_in addr;
|
|
int sockfd;
|
|
int attempts = 0;
|
|
|
|
while (SERVER_PORT == 9191 && attempts++ < 5)
|
|
{
|
|
// Create a socket
|
|
sockfd = socket( AF_INET, SOCK_STREAM, 0 );
|
|
|
|
// Setup its address structure
|
|
bzero( &addr, sizeof(struct sockaddr_in));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = htonl( INADDR_ANY );
|
|
addr.sin_port = htons( 0 );
|
|
|
|
// Bind it to an address and port
|
|
bind( sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr));
|
|
|
|
// Set it listening for connections
|
|
listen( sockfd, 5 );
|
|
|
|
// Find out what port the socket was bound to
|
|
socklen_t namelen = sizeof(struct sockaddr_in);
|
|
getsockname( sockfd, (struct sockaddr *)&addr, &namelen );
|
|
|
|
if (addr.sin_port <= 1024)
|
|
addr.sin_port = 9191;
|
|
else
|
|
SERVER_PORT = addr.sin_port;
|
|
|
|
shutdown(sockfd, 2);
|
|
}
|
|
|
|
if (SERVER_PASSWORD == nil)
|
|
{
|
|
CFUUIDRef uuidObj = CFUUIDCreate(nil);
|
|
SERVER_PASSWORD = (NSString*)CFUUIDCreateString(nil, uuidObj);
|
|
CFRelease(uuidObj);
|
|
}
|
|
|
|
if (SERVER_USER == nil)
|
|
{
|
|
CFUUIDRef uuidObj = CFUUIDCreate(nil);
|
|
SERVER_USER = (NSString*)CFUUIDCreateString(nil, uuidObj);
|
|
CFRelease(uuidObj);
|
|
// usernames can't have "-" in them
|
|
SERVER_USER = [[SERVER_USER autorelease] stringByReplacingOccurrencesOfString:@"-" withString:@""];
|
|
}
|
|
|
|
// setup environment variables
|
|
NSDictionary *env = [[[NSProcessInfo processInfo] environment] mutableCopy];
|
|
[env setValue:[mainBundle resourcePath] forKey:@"NATIVEHOST_RESOURCES"];
|
|
[env setValue:[[NSNumber numberWithInt:SERVER_PORT] stringValue] forKey:@"NATIVEHOST_SERVER_PORT"];
|
|
[env setValue:SERVER_USER forKey:@"NATIVEHOST_SERVER_USER"];
|
|
[env setValue:SERVER_PASSWORD forKey:@"NATIVEHOST_SERVER_PASSWORD"];
|
|
|
|
// create pipes for stdout and stderr
|
|
NSPipe * outputPipe = [NSPipe pipe];
|
|
outputFile = [outputPipe fileHandleForReading];
|
|
NSPipe * errorPipe = [NSPipe pipe];
|
|
errorFile = [errorPipe fileHandleForReading];
|
|
|
|
// setup the process
|
|
process = [[NSTask alloc] init];
|
|
[process setLaunchPath:serverPath];
|
|
[process setStandardOutput:outputPipe];
|
|
[process setStandardError:errorPipe];
|
|
[process setEnvironment:env];
|
|
[env release];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(serverDidTerminate:)
|
|
name:NSTaskDidTerminateNotification
|
|
object:process];
|
|
|
|
[process launch];
|
|
|
|
BOOL didStartup = NO;
|
|
NSData * data = nil;
|
|
|
|
while (!didStartup && (data = [outputFile availableData]) && [data length])
|
|
{
|
|
NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
if (string)
|
|
{
|
|
NHLog(@"SERVER-OUT", string);
|
|
|
|
didStartup = [string rangeOfString:@"Jack is starting up"].location != NSNotFound;
|
|
|
|
[string release];
|
|
}
|
|
}
|
|
NSLog(@"Server has started.");
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver:self
|
|
selector:@selector(didReadStdOut:)
|
|
name:NSFileHandleReadCompletionNotification
|
|
object:outputFile];
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver:self
|
|
selector:@selector(didReadStdErr:)
|
|
name:NSFileHandleReadCompletionNotification
|
|
object:errorFile];
|
|
|
|
[outputFile readInBackgroundAndNotify];
|
|
[errorFile readInBackgroundAndNotify];
|
|
|
|
return didStartup;
|
|
}
|
|
|
|
- (void)stop
|
|
{
|
|
[process terminate];
|
|
}
|
|
|
|
- (void)didReadStdOut:(NSNotification *)aNotification
|
|
{
|
|
NSString * string = [[NSString alloc] initWithData:[[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem]
|
|
encoding:NSASCIIStringEncoding];
|
|
NHLog(@"SERVER-OUT", string);
|
|
[string release];
|
|
[outputFile readInBackgroundAndNotify];
|
|
}
|
|
|
|
- (void)didReadStdErr:(NSNotification *)aNotification
|
|
{
|
|
NSString * string = [[NSString alloc] initWithData:[[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem]
|
|
encoding:NSASCIIStringEncoding];
|
|
NHLog(@"SERVER-ERR", string);
|
|
[string release];
|
|
[errorFile readInBackgroundAndNotify];
|
|
}
|
|
|
|
- (void)serverDidTerminate:(NSNotification *)note
|
|
{
|
|
if ([process terminationStatus] != 0)
|
|
{
|
|
NSString *appName = [[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:@"CFBundleDisplayName"];
|
|
if (appName == nil)
|
|
appName = [[NSProcessInfo processInfo] processName];
|
|
|
|
if (SERVER_PORT != 9191 && [self start])
|
|
{
|
|
NSRunAlertPanel(@"Unexpected Error",
|
|
[NSString stringWithFormat:@"%@ was not able to complete the last action you performed. Try again, and report the problem if it continues to occur.", appName],
|
|
@"OK", nil, nil);
|
|
}
|
|
else
|
|
{
|
|
NSRunAlertPanel(@"Unexpected Error",
|
|
[NSString stringWithFormat:@"A fatal error occurred. %@ could not recover and must terminate.", appName],
|
|
@"Terminate", nil, nil);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[process release];
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|