/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : Supervisor.cc                                                 */
/* Author   : Tom O'Reilly                                                  */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/sched.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <unistd.h>
#include "Supervisor.h"
#include "TaskNames.h"
#include "LayeredControlIF.h"
#include "ExternalCommsIF.h"
#include "Syslog.h"
#include "System.h"
#include "TimeP.h"
#include "FastTime.h"
#include "StringConverter.h"
#include "SupervisorOutput.h"

#define MaxExecArgs 10

static Supervisor *Supervisor::_theSupervisor = 0;
static FastTime* Supervisor::_fastTime=NULL;
sig_atomic_t signaled = 0;
sig_atomic_t signal_quit = 0;

Supervisor::Supervisor(int argc, char **argv)
{
    Boolean debug=True;
    // Keep track of the most recently created Supervisor for cleanup
    // on signal/exit
    _theSupervisor = this;
    _output = new SupervisorOutput();
    _dynoStr = "";
    
    if (parseOptions(argc, argv) == -1)
        exit(1);
}

Supervisor::~Supervisor()
{
    Boolean debug=True;
    dprintf("Supervisor::~Supervisor - dtor\n");

    free((void *)_planFileName);
    free((void *)_abortFileName);
    free((void *)_deviceFileName);
    // check and delete (normally deleted in shutdown)
    if (_output){
        dprintf("Supervisor::~Supervisor - deleting _output [%p]\n",_output);
        delete _output;
    }
    
    if (Supervisor::_fastTime) {
        dprintf("Supervisor::~Supervisor - deleting FastTime [%p]\n",Supervisor::_fastTime);
        delete Supervisor::_fastTime;
    }
}

// Signal child processes to quit, do final memory cleanup
// optionally, call exit
void Supervisor::shutdown(int exitCode, Boolean doExit)
{
    Boolean debug = True;
    // ensure that cleanup and signal code invoked only once
    static Boolean has_run=False;
    
    if (! has_run) {
        dprintf("Supervisor::shutdown - signaling processes\n");
        kill(0, SIGINT);
        
        dprintf("Supervisor::shutdown - cleaning up\n");

        // should cleanup the FastTime object, as needed
        if (Supervisor::_fastTime) {
            dprintf("Supervisor::shutdown - deleting FastTime [%p]\n",Supervisor::_fastTime);
            delete Supervisor::_fastTime;
        }
        
        // once this is deleted, no more writes to syslog
        // [dprintf should work, but may come out after last syslog output]
        if (_theSupervisor && _theSupervisor->_output) {
            dprintf("Supervisor::shutdown - deleting output\n");
            delete _theSupervisor->_output;
            _theSupervisor->_output=NULL;
        }
    }else{
        dprintf("Supervisor::shutdown - already run\n");
    }
    
    has_run=True;
    
    if (doExit) {
        dprintf("Supervisor::shutdown - exiting\n");
        exit(exitCode);
    }
    
    dprintf("Supervisor::shutdown - done\n");
}

int Supervisor::checkTasks(Boolean restart)
{
    int nDied = 0;
    Boolean debug = True;
    TaskEntry *task;
    for (int i = 0; i < _tasks.size(); i++) {
        _tasks.get(i, &task);
        // Check to see if process exists
        if (kill(task->_pid, 0) == -1) {
            if (errno == ESRCH) {
                // Task isn't there anymore!
                Syslog::write("Task \"%s\" died?\n", task->_interface->name());
                nDied++;
                if (restart)
                    createTask(task->_interface->name(), task->_args, True);
            }
            
        }
    }
    return nDied;
}


void Supervisor::keepAlive()
{
  Boolean debug = False;
  dprintf("Supervisor::keepAlive()\n");
}

int Supervisor::createCoreTasks()
{
    char args[100];
    Boolean debug = True;
    fprintf(stderr, "Supervisor::createCoreTasks()\n");
    
    
    if (createTask(EventLogName, "", False) == -1) {
        Syslog::write("Couldn't start " EventLogName);
        return -1;
    }
    
    if (createTask(WorkSiteName, "", False) == -1) {
        Syslog::write("Couldn't start " WorkSiteName);
        return -1;
    }
    
    if (createTask(VehicleConfigurationName, "", False) == -1) {
        Syslog::write("Couldn't start " VehicleConfigurationName);
        return -1;
    }
    //
    // This task is now started by devices.cfg.  It needs to know the serial
    // port.
    //
#if 0
    if (createTask(DropWeightName, "", False) == -1) {
        Syslog::write("Couldn't start " DropWeightName);
        return -1;
    }
#endif
    
    if (createTask(ExternalCommsName, "", False) == -1) {
        Syslog::write("Couldn't start " ExternalCommsName);
        return -1;
    }
    
    if (createTask(NavigationName, "", False) == -1) {
        Syslog::write("Couldn't start " NavigationName);
        return -1;
    }
    
    sprintf(args, "%s -plan %s -abort %s", _dynoStr, _planFileName,
            _abortFileName);
    Syslog::write("starting LC: %s %s", LayeredControlName, args);
    
    if (createTask(LayeredControlName, args, False) == -1) {
        Syslog::write("Couldn't start " LayeredControlName);
        return -1;
    }
    
    //sleep(5);
    
    if (createTask(DynamicControlName, "", False) == -1) {
        Syslog::write("Couldn't start " DynamicControlName);
        return -1;
    }
    
    return 0;
    
}

int Supervisor::createSimTasks()
{
    int err_count=0;
    
    if (!_simulated) {
        return -1;
    }
    
    if (createTask(SimulatorName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatorName);
        return -1;
    }
    
    if (createTask(SimulatedDepthSensorName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedDepthSensorName);
        return -1;
    }
#if 0
    if (createTask(SimulatedRangefinderName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedRangefinderName);
        return -1;
    }
#endif
    
    char *configDirPath = strdup(getenv(AuvConfigDirName));
    Syslog::write("Supervisor: configDirPath = %s", configDirPath);
    //
    // Parse out the name of the configuration directory, without the path. See
    // also utils/td.cc.
    char *configDirPtr = configDirPath;
    char *token, *namePtr;
    while ((token = strtok(configDirPtr, "/")) != 0)
    {
        namePtr = token;
        configDirPtr = 0;
    }
    //
    // namePtr now points to the filename stored at the end of the
    // string configDirPath.
    //
    // if( !strcmp( namePtr, "multibeam" ) )
    // {
    //    // Create the simulated Kearfott task here.
    //    Syslog::write("Supervisor:: This is the multibeam sim!");
    // }
    // else create the simulated AHRS.
    if (createTask(SimulatedKearfottName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedKearfottName);
        return -1;
    }
    
    if (createTask(SimulatedAHRSName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedAHRSName);
        return -1;
    }
    
    if (createTask(SimulatedTailConeName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedTailConeName);
        return -1;
    }
    
    if (createTask(SimulatedGpsName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedGpsName);
        return -1;
    }
    
    if (createTask(SimulatedDvlName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedDvlName);
        return -1;
    }
    
    if (createTask(SimulatedDvlSideName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedDvlSideName);
        return -1;
    }
    
    if (createTask(SimulatedDeltaTName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedDeltaTName);
        return -1;
    }
    
    if (createTask(SimulatedCtdName, "", False) == -1)
    {
        Syslog::write("Couldn't start " SimulatedCtdName);
        return -1;
    }
    if (createTask(SimulatedBfBattName, "", False) == -1)
    {
        Syslog::write("Couldn't start " SimulatedBfBattName);
        return -1;
    }
    if (createTask(SimulatedHydroscatName, "", False) == -1)
    {
        Syslog::write("Couldn't start " SimulatedHydroscatName);
        return -1;
    }
    if (createTask(SimulatedIsusName, "", False) == -1)
    {
        Syslog::write("Couldn't start " SimulatedIsusName);
        return -1;
    }
    
    if (createTask(SimulatedGulperName, "", False) == -1)
    {
        Syslog::write("Couldn't start " SimulatedGulperName);
        return -1;
    }
    
    if (createTask(SimulatedResonName, "", False) == -1)
    {
        Syslog::write("Couldn't start " SimulatedResonName);
        return -1;
    }
    
    if (createTask(SimulatedBenthosModemName, "", False) == -1)
    {
        Syslog::write("Couldn't start " SimulatedBenthosModemName);
        return -1;
    }
    
    //
    // Create additional simulation tasks for the BI vehicle:
    if (createTask(SimulatedUsblName, "", False) == -1)
    {
        Syslog::write("Couldn't start " SimulatedUsblName);
        return -1;
    }
    
    if (_trn)
    {
        // fastSim uses a simulated TerrainAidServer.
        if( _fastSim )
        {
            if (createTask(SimulatedTerrainAidName, "", False) == -1)
            {
                Syslog::write("Couldn't start " SimulatedTerrainAidName);
                return -1;
            }
        }
        // For real-time simulation, use the original TerrainAid Server/Driver pair.
        else
        {
            if (createTask(TerrainAidServerName, "", False) == -1)
            {
                Syslog::write("Couldn't start " TerrainAidServerName);
                return -1;
            }
        }
    }
    
    Boolean runCathx = False;
    Boolean runCarl  = False;
    
    if( runCathx && !_fastSim )
    {
        if (createTask("cathxServer", "", False) == -1)
        {
            Syslog::write("Couldn't start " "cathxServer");
            return -1;
        }
    }
    else
    {
        if (createTask("simCathx", "", False) == -1)
        {
            Syslog::write("Couldn't start " "simCathx");
            return -1;
        }
    }
    
    if(runCarl && !_fastSim)
    {
        if (createTask("carlServer", "", False) == -1)
        {
            Syslog::write("Couldn't start " "carlServer");
            return -1;
        }
    }
    else
    {
        if (createTask("simCarl", "", False) == -1)
        {
            Syslog::write("Couldn't start " "simCarl");
            return -1;
        }
    }
    
    if (createTask(CameraServerName, "-dev tcp:172.20.64.214:10002 -sim",
                   False) == -1)
    {
        Syslog::write("Couldn't start " CameraServerName);
        return -1;
    }
    
    //
    // Since there is no hardware, a simulation version isn't needed and we can
    // run the real thing here.
    //
#if 0
    if (createTask(AdaptiveSamplerName, "", False) == -1)
    {
        Syslog::write("Couldn't start " AdaptiveSamplerName);
        return -1;
    }
#endif
    
    if (createTask(SimulatedEchoSounderName, "", False) == -1) {
        Syslog::write("Couldn't start " SimulatedEchoSounderName);
        return -1;
    }
    
    if (createTask("sysDataDriver", "-f5", False) == -1) {
        Syslog::write("Couldn't start " "sysDataDriver.");
        return -1;
    }
    
    // Cluster is required for Autonomy simulation
#ifdef false
    if (createTask(ClusterServerName, "", False) == -1)
    {
        Syslog::write("Couldn't start " ClusterServerName);
        return -1;
    }
    
    if (createTask(SmartSamplerServerName, "", False) == -1)
    {
        Syslog::write("Couldn't start " SmartSamplerServerName);
        return -1;
    }
#endif
    
    //
    // For the Multibeam only!  It requires Sib's new power board.  That is,
    // the dropWeightServer must be run on the vehicle with the power board
    // present.
    //
    // We have to do this manually here because the simulation doesn't read the
    // devices.cfg
    //
#if 0
    char *localArgs = "-dev /dev/ser9";
    if (createTask("dropWeightServer", localArgs, False) == -1)
    {
        Syslog::write("Couldn't start the drop weight server." );
        return -1;
    }
    strcpy(_dwArgs, localArgs);
#endif
    //
    // For testing the State Publisher.
    if( strcmp(_dynoStr,"" ) )    //if _dynoStr *doesn't* equal "".  So confusing...
    {
        if(createTask("statePublisher", "", False) == -1)
        {
            Syslog::write("Couldn't start statePublisher" );
            return -1;
        }
    }
    
    return 0;
}

int Supervisor::createRealTasks()
{
  // Read configuration file, usually devices.cfg.  Create specified tasks.
  const char *fileName = System::configurationFile(_deviceFileName);

  return createTasks(fileName);

}


int Supervisor::createTasks(const char *fileName)
{

    Boolean debug = False;
    char errorBuf[256];
    
    FILE *fp = fopen(fileName, "r");
    if (fp == 0) {
        sprintf(errorBuf, "Supervisor::createTasks() - \n"
                "couldn't open task config file \"%s\"", fileName);
        
        throw Exception(errorBuf);
    }
    
    System::copyToLogDir(fileName);
    
    char buf[512];
    
    while (fgets(buf, sizeof(buf), fp) != 0) {
        
        char *ptr;
        
        if ((ptr = strchr(buf, '\r')) != 0)
            // Remove carriage-return
            *ptr = '\0';
        
        if ((ptr = strchr(buf, '\n')) != 0)
            // Remove newline
            *ptr = '\0';
        
        if ((ptr = strchr(buf, '#')) != 0)
            // Ignore everything following comment character
            *ptr = '\0';
        
        int nToken = 0;
        char *token;
        char *programName;
        char args[512];
        args[0] = '\0';
        char *argsPtr = args;
        ptr = buf;
        while ((token = strtok(ptr, " \t")) != 0) {
            ptr = 0;
            
            if (nToken == 0) {
                programName = token;
            }
            else {
                // Argument to program
                sprintf(argsPtr, "%s ", token);
                argsPtr += strlen(argsPtr);
                if (argsPtr > (args + sizeof(args))) {
                    sprintf(errorBuf, "Supervisor::createRealTasks() - \n"
                            "Overflow of command argument buffer");
                    
                    fclose(fp);
                    throw Exception(errorBuf);
                }
            }
            nToken++;
        }
        if (nToken) {
            debug=True;
            dprintf("Supervisor::createTasks() - "
                    "spawning %s %s", programName, args);
            debug=False;
            
            // Spawn the program
            if (createTask(programName, args, False) == -1) {
                sprintf(errorBuf, "Supervisor::createTasks() - \n"
                        "Exec of \"%s %s\" failed", programName, args);
                
                fclose(fp);
                throw Exception(errorBuf);
            }
            //
            // Save the args for the drop weight server so that we can restart it
            // after the mission if necessary.
            //
            if( !strcmp( programName, "dropWeightServer" ) ) strcpy( _dwArgs, args );
        }
    }
    
    fclose(fp);
    return 0;
}


int Supervisor::createTask(const char *taskName, const char *args,
			   Boolean restarted)
{
    Boolean debug = False;
    
    fprintf(stderr, "Supervisor::createTask() - %s %s\n", taskName, args);
    
    char buf[512];
    strcpy(buf, args);
    
    // Parse argument string into argv array
    const char *argv[MaxExecArgs];
    argv[0] = taskName;
    char *ptr = buf;
    char *token;
    int nArgs = 1;
    while ((token = strtok(ptr, " \t")) != 0) {
        ptr = 0;
        if (nArgs >= MaxExecArgs - 1) {
            // Too many arguments (note: need to reserve last arg for
            // "restart" option)
            Syslog::write("Supervisor::createTask() - %s %s - too many arguments\n",
                          taskName, args);
            shutdown(1, True);
        }
        
        argv[nArgs++] = token;
    }
    
    if (restarted)
        // Tells Task that it needs to re-initialize itself
        argv[nArgs++] = RestartMnem;
    
    // Mark end of argument list
    argv[nArgs] = 0;
    
    for (int i = 0; i < nArgs; i++) {
        dprintf("createTask(), argv[%d]=%s\n", i, argv[i]);
    }
    
    char errorBuf[512];
    pid_t taskPid;
    
    Boolean error = False;
    
    // Create Task process
    switch ((taskPid = fork())) {
            
        case 0:
            // In child; execute Task program
            dprintf("Supervisor::createTask() - execvp(\"%s\")...\n", taskName);
            execvp(taskName, argv);
            sprintf(errorBuf, "Supervisor::createTask() - execvp() of \"%s\" failed",
                    taskName);
            
            perror(errorBuf);
            shutdown(1, True);
            break;
            
        case -1:
            // Fork failed
            perror("Supervisor::createTask() - fork() failed");
            error = True;
            break;
            
        default:
            // In parent; add TaskEntry to table
            dprintf("Supervisor::createTask() - parent task; add TaskEntry\n");
            addTaskEntry(taskName, taskPid, args);
            dprintf("Supervisor::createTask() - added TaskEntry.\n");
    }
    
    dprintf("Supervisor::createTask() - done with switch\n");
    if (error)
        return -1;
    else
        return 0;
}


void Supervisor::addTaskEntry(const char *taskName, pid_t pid, 
			      const char *args)
{
  Boolean debug = False;
  TaskEntry *taskEntry;
  
  // Check to see whether task is already in table
  for (int i = 0; i < _tasks.size(); i++) {
    _tasks.get(i, &taskEntry);
    if (!strcmp(taskName, taskEntry->_interface->name())) {
      // Task is already in list. Use same interface, just replace pid
      taskEntry->_pid = pid;
      _tasks.set(i, &taskEntry);
      return;
    }
  }

  // Task is not yet in list; add it
  TaskInterface *interface = new TaskInterface(taskName, taskName, 
					       DoNotConnect);

  taskEntry = new TaskEntry(pid, interface, args);
  _tasks.add(&taskEntry);
}

// do all the stuff that shouldn't be done in ctor, but was
int Supervisor::initialize()
{
    Boolean debug=True;
    
#ifdef FASTTIME
    // Fastsim option used? Setup the FastSim shared memory
    //
    if (_fastSim){
        // create and initialize FastTime shared memory
        Supervisor::_fastTime=new FastTime();
        Supervisor::_fastTime->initialize();
        dprintf("\n****Supervisor::Supervisor using FastTime instance [%p]\n",Supervisor::_fastTime);
    }
    // explicitly set FastTime environment variable
    // whether enabled or not.
    // Users (Time, Simlator, Navigation) check this var and
    // create FastTime objects (or not) as needed
    putenv((_fastTime?FAST_TIME_SET_EN:FAST_TIME_SET_DI));
    
    dprintf("\n>>>>Supervisor::Supervisor - FAST_TIME_EN[%s]\n",getenv(FAST_TIME_ENV));
#endif
    
    
    dprintf("Supervisor::Supervisor() - simulated=%d, realTailCone=%d",
            _simulated, _realTailCone);
    
    // [however, probably not a good idea to handle signals in ctor]
    struct sigaction act;
    sigset_t set;
    
    sigemptyset(&set);
    sigaddset( &set, SIGINT);
    sigaddset( &set, SIGTERM);
    sigaddset( &set, SIGQUIT);
    act.sa_handler = Supervisor::signalHandler;
    act.sa_mask = set;
    act.sa_flags = 0;
    
    sigaction(SIGINT, &act, NULL);
    sigaction(SIGTERM, &act, NULL);
    sigaction(SIGQUIT, &act, NULL);
    
    // We don't wait for children to die, and we don't
    // want any zombies either...
    signal(SIGCHLD, SIG_IGN);
    
    
    //
    // Check to see if the dropWeight driver or server is running.  If so, kill
    // them. We will rely on the devices.cfg file to restart them.  They must
    // be restarted so that they're then a member of the process group, so that
    // Supervisor can kill them with the general kill(0, SIGINT) command down
    // in shutdown().
    //
    // The code below has two ways to kill them.  The popen code returns the
    // pid if the process exists.
    //
    int rc;
#if 0
    FILE *ppipe;
    char buf[512];
    dprintf("Slay dropWeightServer.\n");
    ppipe = popen("slay dropWeightServer","r");
    rc = fscanf( ppipe, "%s", buf );
    buf[511]='\0';
    if( rc != -1 )
        Syslog::write("Supervisor has slain dropWeightServer, pid = %s.", buf);
    else
        Syslog::write("Supervisor can't find process dropWeightServer.");
    pclose(ppipe);
    
    ppipe = popen("slay dropWeight","r");
    rc = fscanf( ppipe, "%s", buf );
    buf[511]='\0';
    if( rc != -1 )
        Syslog::write("Supervisor has slain dropWeight, pid = %s.", buf);
    else
        Syslog::write("Supervisor can't find process dropWeight.");
    pclose(ppipe);
    //
    // Testing shows that rc is -1, even if the slay command worked.  This
    // wasn't the case in the stand-alone test.  The "system" command works
    // better, so I'll use that way for now.
    //
#else
    rc = system("slay -Q dropWeightServer");
    if( rc ) Syslog::write("Supervisor has slain dropWeightServer.");
    else dprintf("Supervisor can't find process dropWeightServer.\n");
    rc = system("slay -Q dropWeight");
    if( rc ) Syslog::write("Supervisor has slain dropWeight.");
    else dprintf("Supervisor can't find process dropWeight.\n");
#endif
    
    // Set the default weight drop to True.  SIGTERM | SIGQUIT will
    // set it to false.
    _dropIt = True;
    _dwArgs[0] = '\0';
    
    // Create data log subdirectory for this run
    createLogDir();
    
    // Create core system tasks
    if(createCoreTasks()){
        return -1;
    }
    
    if (_simulated) {
        if(createSimTasks()){
            return -1;
        }
    }
    else {
        // Create auxillary tasks.  This reads devices.cfg.
        if(createRealTasks()){
            return -1;
        }

    }
    
    _running = False;
    
    //_startTime = time(0);
    
    struct timespec ts;
    Time::gettime(&ts);
    _startTime = ts.tv_sec;
    
    _beenHere = False;
    return 0;
}

void Supervisor::run() 
{
    Boolean debug=True;
    
    if(initialize()==0){
        
        _running = True;
        while (True) {
            
            // Check status of Tasks
            if (checkTasks(True) > 0) {
                // Something died
                Syslog::write("Supervisor::run - task(s) died - exiting run loop\n");
                break;
            }
            
            // Reset watchdog timer
            // keepAlive();
            // Note:  2004/2/20 This has been replaced by the drop weight driver.
            sleep(2);
            
            if (signaled) {
                // caught a signal
                Syslog::write("Supervisor::run - caught stop signal - exiting run loop\n");
                break;
            }
        }
    }else{
        dprintf("Supervisor:: initialization failed - exiting\n");
    }
    // shutdown (don't call exit, just return)
    Syslog::write("Supervisor::run - calling shutdown/exit\n");
    shutdown(1, False);
}

void Supervisor::createLogDir()
{
    Boolean debug = False;
    
    char errorBuf[256];
    
    char *auvLogDir = getenv(AuvLogDirName);
    
    if (auvLogDir == 0) {
        
        Syslog::write("Environment variable %s not set\n",
                      AuvLogDirName);
        
        shutdown(1,False);
        exit(1);
    }
    
    //time_t timeNow = time(0);
    // Determine current year and day-of-year
    struct timespec ts;
    Time::gettime(&ts);
    time_t timeNow = ts.tv_sec;
    struct tm *now = gmtime(&timeNow);
    
    int currentYear = now->tm_year + 1900;
    int currentDoy = now->tm_yday + 1;
    
    DIR *directory;
    
    if ((directory = opendir(auvLogDir)) == 0) {
        Syslog::write("Can't open log directory \"%s\"", auvLogDir);
        sprintf(errorBuf, "Supervisor::createLogDir(), openDir(): ",
                strerror(errno));
        Syslog::write(errorBuf);
        shutdown(1,False);
        exit(1);
    }
    
    int maxRun = -1;
    
    char path[256];
    struct dirent *entry;
    
    while ((entry = readdir(directory)) != 0) {
        
        sprintf(path, "%s/%s", auvLogDir, entry->d_name);
        
        struct stat fileStat;
        
        if (stat(path, &fileStat) == -1) {
            
            sprintf(errorBuf, "Supervisor::createLogDir(), stat(%s):\n%s",
                    path, strerror(errno));
            
            Syslog::write(errorBuf);
            shutdown(1,False);
            exit(1);
        }
        
        if (!S_ISDIR(fileStat.st_mode)) {
            dprintf("file %s is NOT a directory\n", entry->d_name);
            continue;
        }
        
        // Parse year, day-of-year, and run # from file name
        strcpy(path, entry->d_name);
        char *pathPtr = path;
        int nToken = 0;
        Boolean reject = False;
        char *token;
        
        while ((token = strtok(pathPtr, ".")) != 0 && !reject) {
            pathPtr = 0;
            
            // Determine if token conforms to log subdirectory name format
            if (!StringConverter::isInteger(token)) {
                break;
            }
            
            
            switch (nToken) {
                    
                case 0:
                    
                    // Year
                    if (StringConverter::stringToInteger(token) != currentYear)
                        reject = True;
                    
                    break;
                    
                case 1:
                    // Day of year
                    if (StringConverter::stringToInteger(token) != currentDoy)
                        reject = True;
                    
                    break;
                    
                case 2:
                    // Run number. If we get here, then year/day-of-year corresponds
                    // to today.
                    maxRun = max(maxRun, StringConverter::stringToInteger(token));
                    break;
                    
                default:
                    reject = True;
            }
            
            nToken++;
        }
    }
    
    closedir(directory);
    
    sprintf(path, "%s/%d.%03d.%02d",
            auvLogDir, currentYear, currentDoy, maxRun + 1);
    
    // Create new directory
    Syslog::write("Create log directory \"%s\"\n", path);
    mode_t mode = 0777;
    if (mkdir(path, mode) == -1) {
        sprintf(errorBuf, "Supervisor::createLogDir(), mkdir(): %s",
                strerror(errno));
        
        Syslog::write(errorBuf);
        shutdown(1,False);
        exit(1);
    }
    
    // Create symbolic link to new directory
    char linkName[256];
    sprintf(linkName, "%s/%s", auvLogDir, LatestLogDirName);
    
    // First delete link (in case it already exists)
    unlink(linkName);
    
    char cmd[256];
    sprintf(cmd, "ln -s %s %s", path, linkName);
    
    // Have to use system() to create link, because QNX link() does not
    // work for directories!!!
    if (system(cmd) != 0) {
        
        sprintf(errorBuf, "Supervisor::createLogDir()\ncommand \"%s\" failed",
                cmd);
        
        Syslog::write(errorBuf);
        shutdown(1,False);
        exit(1);
    }
    
    // Want to 'tee' stderr to a syslog file in 'latest' directory
    char syslogFileName[256];
    sprintf(syslogFileName, "%s/syslog", linkName);
    
    Boolean error = False;
    pid_t child;
    int pipeFd[2];
    
    pipe(pipeFd);
    
    switch ((child = fork())) {
            
        case 0:
            // In child
            // Close stdin
            close(0);
            
            // Get stdin from pipe
            dup(pipeFd[0]);
            close(pipeFd[1]);
            
            // Execute 'tee' with '-i' option, so that signals are properly
            // delivered to other kids in process group.
            execlp("tee", "tee", "-i", syslogFileName, 0);
            perror("Supervisor::createLogDir() - execlp() of \"tee\" failed");
            shutdown(1, True);
            break;
            
        case -1:
            // Fork failed
            perror("Supervisor::createLogDir() - fork() failed");
            error = True;
            break;
            
        default:
            // In parent; 
            // Close stderr
            close(2);
            dup(pipeFd[1]);
            close(pipeFd[0]);
    }
}

void Supervisor::signalHandler(int sigNo)
{
    Boolean debug = True;
    
    Syslog::write("Supervisor::signalHandler - caught signal %d", sigNo);

    // [actually, probably dodgy to call dprintf from sig handler]
    // if already signaled,
    if (signaled) {
        dprintf("Supervisor::signalHandler possible duplicate signal[%d]\n", sigNo);
    }
    // set signal handling busy flag (probably not really necessary, with
    // correct sig masks in place)
    signaled=sigNo;
    
    // set exit flag (detected and handled by run loop)
    // by incrementing, can track number of times called
    signal_quit++;
    Syslog::write("Supervisor::signalHandler - setting exit flag [%d]\n",signal_quit);
    
    return;
}

int Supervisor::parseOptions(int argc, char **argv)
{
  _minRunTime = 300;
  _simulated = False;
  _fastSim   = False;
  _realTailCone = True;
  _trn = False;

  _planFileName = strdup("normalMplan.cfg");
  _abortFileName = strdup("abortMplan.cfg");
  _deviceFileName = strdup("devices.cfg");
  _terminateFileName = 0;

  Boolean error = False;

  for (int i = 1; i < argc; i++) {

    if (!strcmp(argv[i], "-plan")) {
      if (i < argc - 1) {
        free((void *)_planFileName);
        _planFileName = strdup(argv[++i]);
      }
      else
        error = True;
    }

    else if (!strcmp(argv[i], "-abort")) {
      if (i < argc - 1) {
        free((void *)_abortFileName);
        _abortFileName = strdup(argv[++i]);
      }
      else
        error = True;
    }

    else if (!strcmp(argv[i], "-dev")) {
      if (i < argc - 1) {
        free((void *)_deviceFileName);
        _deviceFileName = strdup(argv[++i]);
      }
      else
        error = True;
    }

    else if (!strcmp(argv[i], "-term")) {
      if (i < argc - 1) {
        free((void *)_terminateFileName);
        _terminateFileName = strdup(argv[++i]);
      }
      else
        error = True;
    }

    else if (!strcmp(argv[i], "-minrun") && i < argc - 1) {
      _minRunTime = atoi(argv[++i]);
      if (_minRunTime <= 0) {
        Syslog::write("Invalid time: %s", argv[i]);
        error = True;
      }
    }

    // Deal with simulation options.
    // Fastsim is a kind of simulation, so set standard flags
    // common to both -sim and -fastsim options.
    //
    else if (!strcmp(argv[i], "-sim") || !strcmp(argv[i], "-fastsim")) {
      Syslog::write("Supervisor() - simulating");
      _simulated = True;
      _realTailCone = False;
      _output->data.sim = True;

      // Now, set the fastsim flag when we see the fastsim option.
      // 
      if (!strcmp(argv[i], "-fastsim")) {
        Syslog::write("Supervisor() - going fastsim");
        _fastSim = True;
      }

      _output->write();
    }

    else if (!strcmp(argv[i], "-trn")) {
      _trn = True;
    }

    else if (!strcmp(argv[i], "-simtail")) {
      _simulated = True;
      _realTailCone = True;
    }

    else if (!strcmp(argv[i], "-dyno")) {
       _dynoStr = "-dyno";
    }

    else {
      Syslog::write("Illegal option: %s", argv[i]);
      error = True;
    }
  }

  if (error) {
    Syslog::write("usage: %s "
		  "[-sim][-fastsim][-simtail][-dyno][-plan file][-abort file][-dev file][-term file][-minrun secs]", 
		  argv[0]);
    return -1;
  }
  else
    return 0;
}
