Logo Search packages:      
Sourcecode: adun.app version File versions  Download package

AdServer.m

/*
   Project: AdunServer

   Copyright (C) 2005 Michael Johnston & Jordi Villa-Freixa

   Author: Michael Johnston

   Created: 2005-05-31 15:41:02 +0200 by michael johnston

   This application is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This application is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#include "AdServer.h"
#include <wait.h>

@implementation AdServer

- (void) kernelTermination: (NSNotification*) aNotification
{
      NSError* error;

      /*
       * Core Exit Procedure : 
       * On controlled exit - Posts an exit (error) message with the server via closeConnection: 
       * On uncontrolled exit - Does nothing
       *
       * Here we check if the process posted an error message eariler via closeConnection.
       * If it didn't we create an error using the exit status code and use that instead.
       */

      int status = [[aNotification object] terminationStatus];
      int pid;
      id task, process;
      NSMutableDictionary* errorInfo;

      task = [aNotification object];
      pid = [task processIdentifier];

            NSWarnLog(@"Kernel process %d exited with status %d. (%s)", pid, status, strerror(status));
      
      error = nil;
      NSDebugLLog(@"AdunServer", @"Checking for error from AdunCore");
      if((error = [processErrors objectForKey: [NSNumber numberWithInt: pid]]) != nil)
            NSWarnLog(@"Kernel posted error %@", [error userInfo]);
      else if(status != 0)
      {
            NSDebugLLog(@"AdunServer", @"No error posted - creating one now.");
            errorInfo =  [NSMutableDictionary dictionary];
            [errorInfo setObject: @"Simulation terminated by uncaught signal.\n"
                  forKey: @"NSLocalizedDescriptionKey"];
            [errorInfo setObject: [NSString stringWithFormat:
                              @"Exit code %d - %s.\n", status, strerror(status)]
                  forKey: @"AdDetailedDescriptionKey"];
            [errorInfo setObject: @"Please submit this event along with the system and options used\
 to the support tracker at gna.org/projects/adun.\n"
                  forKey: @"NSRecoverySuggestionKey"];

            error = [NSError errorWithDomain: @"AdCoreErrorDomain"
                        code: 5
                        userInfo: errorInfo];
      }

      NSDebugLLog(@"AdunServer", @"Retrieving relevant process object");
      process = [processes objectForKey: [NSNumber numberWithInt: pid]];
      NSDebugLLog(@"AdunServer", @"Notifying process object of termination");
      [process processDidTerminate: error];
      [processes removeObjectForKey: [NSNumber numberWithInt: pid]];
      [tasks removeObjectForKey: [NSNumber numberWithInt: pid]];
      [state removeObjectForKey: [NSNumber numberWithInt: pid]];
      [processErrors removeObjectForKey: [NSNumber numberWithInt: pid]];
      [interfaces removeObjectForKey: [NSNumber numberWithInt: pid]];
}

- (void) _adunCoreError
{
      [NSException raise: NSInvalidArgumentException
            format: [NSString stringWithFormat: 
                  @"Couldnt execute AdunCore. Set AdunCorePath default to the correct file \
and ensure its executable. Check AdServer.log for more information."]];

}

- (BOOL) _testFileExistsIsExecutable: (NSString*) path
{
      if([[NSFileManager defaultManager] fileExistsAtPath: path])
      {
            if([[NSFileManager defaultManager] isExecutableFileAtPath: path])
                  return YES;
            else
                  return NO;
      }
      else
            return NO;
}

- (void) _redirectOutput
{
      id logFile;

      logFile = [[NSUserDefaults standardUserDefaults] stringForKey: @"LogFile"];
      if(![[NSFileManager defaultManager] isWritableFileAtPath:
             [logFile stringByDeletingLastPathComponent]])
      {
            logFile = [[[NSUserDefaults standardUserDefaults] 
                        volatileDomainForName: NSRegistrationDomain]
                        valueForKey:@"LogFile"];
            NSWarnLog(@"Invalid value for user default 'LogFile'. The specificed directory is not writable");
            NSWarnLog(@"Switching to registered default %@", logFile);
            if(![[NSFileManager defaultManager] 
                  isWritableFileAtPath: [logFile stringByDeletingLastPathComponent]])
            {
                  [NSException raise: NSInternalInconsistencyException
                        format: [NSString stringWithFormat:
                         @"Fallback logfile (%@) not writable. Cannot start.", logFile]];
            }
      } 

      freopen([logFile cString], "w", stderr);
}

- (id) init
{
      id adunFile;
      id portOne, portTwo;

      if((self = [super init]))
      {
            //redirect output
            
            if([[NSUserDefaults standardUserDefaults] boolForKey: @"RedirectOutput"])
                  [self _redirectOutput];
                  
            if([[NSUserDefaults standardUserDefaults] boolForKey: @"AdServerIsDistributed"])
            {

                  GSPrintf(stderr, @"Using socket ports to enable distributed computing.\n");

                  portOne = [NSSocketPort port];
                  portTwo = [NSSocketPort port];
                  connection = [[NSConnection alloc] initWithReceivePort: portOne 
                              sendPort: portTwo];
                  [connection setRootObject: self];
                  if([connection registerName: @"AdunServer" withNameServer:
                               [NSSocketPortNameServer sharedInstance]] == NO)
                  {
                        NSWarnLog(@"Failed to vend!");
                        exit(1);
                  }
            }
            else
            {
                  GSPrintf(stderr, 
                        @"Using default message ports. Distributed computing facilites disabled.\n");
                  
                  portOne = [NSMessagePort port];
                  portTwo = [NSMessagePort port];
                  connection = [[NSConnection alloc] initWithReceivePort: portOne 
                              sendPort: portTwo];
                  [connection setRootObject: self];
                  
                  if([connection registerName: @"AdunServer" withNameServer:
                               [NSMessagePortNameServer sharedInstance]] == NO)
                  {
                        NSWarnLog(@"Failed to vend!");
                        exit(1);
                  }
            }

            [[NSNotificationCenter defaultCenter] addObserver: self
                  selector: @selector(kernelTermination:)
                  name: NSTaskDidTerminateNotification
                  object: nil];

            tasks = [NSMutableDictionary new];
            interfaces = [NSMutableDictionary new];   
            processes = [NSMutableDictionary new];
            state = [NSMutableDictionary new];
            processErrors = [NSMutableDictionary new];

            //see if we can locate the adun core executable

            adunCorePath = [[[NSUserDefaults standardUserDefaults] stringForKey: @"AdunCorePath"] 
                              stringByAppendingPathComponent: @"AdunCore"];
            [adunCorePath retain];

            if(![self _testFileExistsIsExecutable: adunCorePath])
            {
                  NSWarnLog(@"Defaults value for AdunCorePath (%@) is not valid", adunCorePath);
                  adunFile = [[[NSUserDefaults standardUserDefaults] 
                              volatileDomainForName: NSRegistrationDomain]
                              valueForKey:@"AdunCorePath"];
                  
                  if(![adunCorePath isEqual: adunFile])
                  {
                        NSWarnLog(@"Falling back on registered default.");
                        adunCorePath = adunFile;
                        if(![self _testFileExistsIsExecutable: adunCorePath])
                        {
                              NSWarnLog(@"Adun was installed into a non standard directory.");
                              [self _adunCoreError];
                        }
                  }
                  else
                        [self _adunCoreError];  
            }
            else
                  GSPrintf(stderr, @"Found Adun Core Executable at %@\n", adunCorePath);
      }
      
      fflush(stderr);

      return self;
}

- (void) dealloc
{
      [processes release];
      [tasks release];
      [interfaces release];
      [state release];
      [adunCorePath release]; 
      [processErrors release];

      [super dealloc];
}

//Refactor these messages to use execute:error:process

- (NSError*) startSimulation: (id) process;
{
      id task;
      NSMutableDictionary* userInfo;
      NSError* error;

      NSMutableArray* arguments;

      NSDebugLLog(@"AdunServer", @"Recieved a start simulation message");
      NSDebugLLog(@"AdunServer", @"Process Object is %@", [process description]);
      NSDebugLLog(@"AdunServer", @"Found Adun Core Executable at %@", adunCorePath);
      
      NS_DURING
      {
            task = [NSTask launchedTaskWithLaunchPath: adunCorePath
                  arguments: nil];

            [process setProcessIdentifier: [task processIdentifier]];
            [processes setObject: process 
                  forKey: [NSNumber numberWithInt: [task processIdentifier]]];
            [tasks setObject: task 
                  forKey: [NSNumber numberWithInt: [task processIdentifier]]];
            [state setObject: [NSNumber numberWithBool: NO] 
                  forKey: [NSNumber numberWithInt: [task processIdentifier]]];

            return nil;
      }
      NS_HANDLER
      {
            userInfo = [NSMutableDictionary dictionaryWithCapacity: 1];
            [userInfo setObject: 
                  [NSString stringWithFormat: @"Unable to lauch simulation - %@", [localException reason]]
                  forKey: NSLocalizedDescriptionKey];

            return [NSError errorWithDomain: @"AdServerErrorDomain"
                        code: 1
                        userInfo: userInfo];
      }
      NS_ENDHANDLER

      return nil;
}

- (void) haltProcess: (id) process
{
      NSNumber* pid;
      id task;

      pid = [NSNumber numberWithInt: [process processIdentifier]];
      task = [tasks objectForKey: pid];
      [task suspend];
}

- (void) terminateProcess: (id) process
{
      NSNumber* pid;
      id task;

      pid = [NSNumber numberWithInt: [process processIdentifier]];
      task = [tasks objectForKey: pid];
      [task terminate];
}

- (void) restartProcess: (id) process
{
      NSNumber* pid;
      id task;

      pid = [NSNumber numberWithInt: [process processIdentifier]];
      task = [tasks objectForKey: pid];
      [task resume];
}

//Process Core Interface Methods
/*
 *N.B. If you declare in the protocol for DO that an paramter is "out" as in the
pointer to NSError below. It seems you have to assign a value to it or else
you get a seg fault
*/

- (id) execute: (NSDictionary*) commandDict error: (NSError**) error process: (id) process
{
      NSNumber* pid;
      id interface;
      id returnVal;

      NSDebugLLog(@"AdunServer", @"Recieved command request for process %@", process);
      pid = [NSNumber numberWithInt: [process processIdentifier]];
      interface = [interfaces objectForKey:  pid];
      if(interface == nil)
      {
            NSDebugLLog(@"AdunServer", @"The specified process has not provided its interface");
            *error = [NSError errorWithDomain: @"AdServerCommandInterfaceErrorDomain"
                        code: 0
                        userInfo: [NSDictionary dictionaryWithObject:
                              @"The specified process has not provided its interface"
                              forKey:
                              NSLocalizedDescriptionKey]];

            return nil;
      }
      NSDebugLLog(@"AdunServer", @"Retrieved interface. Checking availability", process);

      if([[state objectForKey: pid] boolValue] == NO)
      {
            NSDebugLLog(@"AdunServer", @"The specified process is not accepting requests");
            *error = [NSError errorWithDomain: @"AdServerCommandInterfaceErrorDomain"
                        code: 0
                        userInfo: [NSDictionary dictionaryWithObject:
                              @"The specified process is not accepting requests"
                              forKey:
                              NSLocalizedDescriptionKey]];
            return nil;
      }
      
      NSDebugLLog(@"AdunServer", @"Exectuting command %@", commandDict);

      returnVal = [[[interface execute: commandDict
                  error: error] retain] autorelease];

      return returnVal;
}

00364 - (NSMutableDictionary*) optionsForCommand: (NSString*) name process: (id) process
{
      NSNumber* pid;
      id interface;
      
      pid = [NSNumber numberWithInt: [process processIdentifier]];
      interface = [interfaces objectForKey:  pid];
      if(interface == nil)
            return nil;

      return [interface optionsForCommand: name];
}

00377 - (NSArray*) validCommandsForProcess: (id) process
{
      NSNumber* pid;
      id interface;
      
      pid = [NSNumber numberWithInt: [process processIdentifier]];
      interface = [interfaces objectForKey:  pid];
      if(interface == nil)
            return nil;

      return [interface validCommands];
}

00390 - (void) useInterface: (id) object forProcess: (int) pid
{
      [interfaces setObject: object forKey: [NSNumber numberWithInt: pid]];
}

00395 - (void) acceptingRequests: (int) pid
{
      //Dictionary containing command name and options
      NSMutableDictionary* commandDict = [NSMutableDictionary dictionary];
      //The options for the command
      NSMutableDictionary* options = [NSMutableDictionary dictionary];
      NSError* error;
      id interface, process;

      NSDebugLLog(@"AdunServer", @"Process %d is accepting requests", pid);

      options = [NSMutableDictionary dictionary];
      interface = [interfaces objectForKey:  [NSNumber numberWithInt: pid]];

      NSDebugLLog(@"AdunServer", @"Create output directories");
      
      process = [processes objectForKey: [NSNumber numberWithInt: pid]];
      [options setObject: [process simulationOutputDirectory]
            forKey: @"simulationOutputDirectory"];

      //check if there is a controller    
      
      if([process controllerOutputDirectory] != nil)
            [options setObject: [process controllerOutputDirectory]
                  forKey: @"controllerOutputDirectory"];

      [commandDict setObject: @"setOutputDirectories"
            forKey: @"command"];
      [commandDict setObject: options forKey: @"options"]; 
      
      [interface execute: commandDict error: &error];
      if(error != nil)
            NSLog(@"Error (%@) - %@",
                  [error domain], 
                  [[error userInfo] objectForKey: NSLocalizedDescriptionKey]); 
            
      [options removeAllObjects];
      
      NSDebugLLog(@"AdunServer", @"Load process data");

      [options setObject: @"Server" forKey: @"inputSourceName"];
      [commandDict setObject: @"loadProcessData" forKey: @"command"];
      [commandDict setObject: options forKey: @"options"]; 
      [interface execute: commandDict error: &error];
      if(error != nil)
            NSLog(@"Error (%@) - %@",
                  [error domain], 
                  [[error userInfo] objectForKey: NSLocalizedDescriptionKey]); 
      
      NSDebugLLog(@"AdunServer", @"Load controller");
      
      [commandDict setObject: @"loadController" forKey: @"command"];
      [interface execute: commandDict error: &error];
      if(error != nil)
            NSLog(@"Error (%@) - %@",
                  [error domain], 
                  [[error userInfo] objectForKey: NSLocalizedDescriptionKey]); 

      NSDebugLLog(@"AdunServer", @"Create simulator");

      [commandDict removeObjectForKey: @"options"];
      [commandDict setObject: @"createSimulator" forKey: @"command"];
      [interface execute: commandDict error: &error];
      if(error != nil)
            NSLog(@"Error (%@) - %@",
                  [error domain], 
                  [[error userInfo] objectForKey: NSLocalizedDescriptionKey]); 
      
      NSDebugLLog(@"AdunServer", @"Create system");
      
      [commandDict setObject: @"createSystem" forKey: @"command"];
      [interface execute: commandDict error: &error];
      if(error != nil)
            NSLog(@"Error (%@) - %@",
                  [error domain], 
                  [[error userInfo] objectForKey: NSLocalizedDescriptionKey]); 
      
      NSDebugLLog(@"AdunServer", @"Start simulation");
      
      [commandDict setObject: @"main" forKey: @"command"];
      [interface execute: commandDict error: &error];
      if(error != nil)
            NSLog(@"Error (%@) - %@",
                  [error domain], 
                  [[error userInfo] objectForKey: NSLocalizedDescriptionKey]); 

      [state setObject: [NSNumber numberWithBool: YES] forKey: [NSNumber numberWithInt: pid]];
      
      NSDebugLLog(@"AdunServer", @"Process %d initialised", pid);
}

- (void) closeConnectionForProcess: (int) pid error: (NSError*) error
{
      [interfaces removeObjectForKey: [NSNumber numberWithInt: pid]];
      
      if(error != nil)
            [processErrors setObject: error forKey: [NSNumber numberWithInt: pid]];
}

00494 - (id) getSystemsForProcess: (int) pid
{
      id process;

      process = [processes objectForKey: [NSNumber numberWithInt: pid]];
      
      return [process valueForKey: @"systems"];
}

00503 - (bycopy NSMutableDictionary*) getOptionsForProcess: (int) pid
{
      id process;

      process = [processes objectForKey: [NSNumber numberWithInt: pid]];
      return [process transmitOptionsForProcess: 0];
}

- (void) controllerData: (id) results forProcess: (int) pid
{
      id  process;
      
      process = [processes objectForKey: [NSNumber numberWithInt: pid]];
      [process setControllerResults: results];
}

@end

Generated by  Doxygen 1.6.0   Back to index