////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2008 The Regents of the University of California
//
// This file is part of Qbox
//
// Qbox is distributed 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.
// See the file COPYING in the root directory of this distribution
// or .
//
////////////////////////////////////////////////////////////////////////////////
//
// UserInterface.C: definition of readCmd and processCmds
//
////////////////////////////////////////////////////////////////////////////////
#include "UserInterface.h"
#include "qbox_xmlns.h"
#include
#include
#include
#include
#include // fsync()
#include // fopen(), fclose(), fprintf()
#include
#include // stat()
#include
using namespace std;
////////////////////////////////////////////////////////////////////////////////
void wait_for_no_file(const string& lockfilename)
{
//cerr << " waiting for no " << lockfilename << endl;
struct stat statbuf;
int status;
do
{
// stat returns 0 if the file exists
status = stat(lockfilename.c_str(),&statbuf);
usleep(100000);
// cerr << ".";
}
while ( status == 0 );
}
////////////////////////////////////////////////////////////////////////////////
UserInterface::UserInterface(void) : terminate_(false)
{
int mype = 0;
MPI_Comm_rank(MPI_COMM_WORLD,&mype);
onpe0_ = ( mype == 0 );
}
////////////////////////////////////////////////////////////////////////////////
UserInterface::~UserInterface(void)
{
std::list::iterator cmd;
for ( cmd = cmdlist.begin(); cmd != cmdlist.end(); cmd++ )
delete *cmd;
std::list::iterator var;
for ( var = varlist.begin(); var != varlist.end(); var++ )
delete *var;
}
////////////////////////////////////////////////////////////////////////////////
int UserInterface::readCmd(string& s, istream &is, bool echo)
{
getline(is,s);
if ( is.eof() )
return 0;
// process line continuation if last character is '\'
while ( (s.size() > 0) && (s[s.size()-1] == '\\') )
{
s[s.size()-1] = ' ';
string stmp;
getline(is,stmp);
s += stmp;
}
if ( echo )
cout << "" << s << "" << endl;
// remove comments before processing
string::size_type pos = s.find_first_of("#");
if ( pos != string::npos )
s = s.substr(0,pos);
#ifdef DEBUG
cout << "readCmd: after removing comments: @" << s << "@" << endl;
#endif
// remove leading white space
pos = s.find_first_not_of(" \t");
if ( pos != string::npos )
s = s.substr(pos);
else
s.clear();
#ifdef DEBUG
cout << "readCmd: after removing leading space: @" << s << "@" << endl;
#endif
// remove trailing white space
pos = s.find_last_not_of(" \t");
if ( pos != string::npos )
{
pos++;
s = s.substr(0,pos);
}
#ifdef DEBUG
cout << "readCmd: after removing trailing space: @" << s << "@" << endl;
#endif
return 1; // a command was read into s
}
////////////////////////////////////////////////////////////////////////////////
void UserInterface::processCmds(istream &cmdstream, const string prompt,
bool echo)
{
#if DEBUG
cout << "UserInterface::processCmds: prompt="
<< prompt << " echo=" << echo << endl;
#endif
// read and process commands from cmdstream until done
string cmdline;
list::iterator cmd;
int done = 0, cmd_read = 0;
if ( onpe0_ )
cout << prompt << " ";
while ( !done )
{
if ( onpe0_ )
{
// readCmd returns 1 if a command is read, 0 if at EOF
cmd_read = readCmd(cmdline, cmdstream, echo );
done = !cmd_read;
}
MPI_Bcast(&cmd_read,1,MPI_INT,0,MPI_COMM_WORLD);
if ( cmd_read )
{
int n = cmdline.size();
MPI_Bcast(&n,1,MPI_INT,0,MPI_COMM_WORLD);
char* buf = new char[n];
if ( onpe0_ )
strncpy(buf,cmdline.c_str(),n);
MPI_Bcast(buf,n,MPI_CHAR,0,MPI_COMM_WORLD);
cmdline = string(buf,n);
delete [] buf;
execCmd(cmdline,prompt);
}
if ( onpe0_ )
done |= terminate_;
MPI_Bcast(&done,1,MPI_INT,0,MPI_COMM_WORLD);
}
if ( onpe0_ )
cout << " End of command stream " << endl;
}
////////////////////////////////////////////////////////////////////////////////
void UserInterface::processCmdsServer ( string inputfilename,
string outputfilename, const string prompt, bool echo)
{
// process commands in server mode
// read commands from inputfilename
// redirect cout to outputfilename
string cmdline;
list::iterator cmd;
int done = 0, cmd_read = 0;
string lockfilename = inputfilename + ".lock";
// in server mode, redirect output to stream qbout
streambuf *cout_buf;
ifstream qbin;
ostringstream os;
while ( !done )
{
if ( onpe0_ )
{
// create file to signal that Qbox is waiting for a command on qbin
FILE *lockfile = fopen(lockfilename.c_str(),"w");
fprintf(lockfile,"1");
fclose(lockfile);
fsync(fileno(lockfile));
usleep(100000);
wait_for_no_file(lockfilename.c_str());
qbin.open(inputfilename.c_str());
qbin.sync();
qbin.clear();
// clear os
os.str("");
// save copy of cout streambuf
cout_buf = cout.rdbuf();
// redirect cout to os
cout.rdbuf(os.rdbuf());
// cerr << " processCmdsServer: cout streambuf redirected" << endl;
cout << "" << endl;
cout << "" << endl;
}
do
{
if ( onpe0_ )
{
// readCmd returns 1 if a command is read, 0 if at EOF
cmd_read = readCmd(cmdline, qbin, echo);
cout << prompt << " " << cmdline << endl;
}
MPI_Bcast(&cmd_read,1,MPI_INT,0,MPI_COMM_WORLD);
if ( cmd_read )
{
int n = cmdline.size();
MPI_Bcast(&n,1,MPI_INT,0,MPI_COMM_WORLD);
char* buf = new char[n];
if ( onpe0_ )
strncpy(buf,cmdline.c_str(),n);
MPI_Bcast(buf,n,MPI_CHAR,0,MPI_COMM_WORLD);
cmdline = string(buf,n);
delete [] buf;
execCmd(cmdline,prompt);
}
if ( onpe0_ )
done |= terminate_;
MPI_Bcast(&done,1,MPI_INT,0,MPI_COMM_WORLD);
} while ( !done && cmd_read );
if ( onpe0_ )
{
qbin.close();
cout << " End of command stream " << endl;
cout << "" << endl;
cout.flush();
// write ostringstream contents to output file
FILE *qboutfile = fopen(outputfilename.c_str(),"w");
fprintf(qboutfile,"%s",os.str().c_str());
fclose(qboutfile);
fsync(fileno(qboutfile));
// restore cout streambuf
cout.rdbuf(cout_buf);
// cerr << " processCmdsServer: cout streambuf reassigned" << endl;
}
// wait before retrying
usleep(200000);
} // while !done
if ( onpe0_ )
{
// remove lock file
remove(lockfilename.c_str());
}
}
////////////////////////////////////////////////////////////////////////////////
void UserInterface::execCmd(string cmdline, string prompt)
{
// check if line contains only white space or comments
if ( cmdline.size() == 0 )
{
#ifdef DEBUG
cout << " empty command line" << endl;
#endif
if ( onpe0_ )
cout << prompt << " ";
}
else if ( cmdline[0] == '!' )
{
// shell escape commands start with '!'
if ( onpe0_ )
{
// remove '!' character
cmdline[0] = ' ';
#ifdef DEBUG
cout << " shell escape: " << cmdline.c_str() << endl;
#endif
system( cmdline.c_str() );
cout << prompt << " ";
}
}
else
{
#ifdef DEBUG
cout << " command is: " << cmdline.c_str() << endl;
#endif
// cout << " command split in the following tokens:" << endl;
// scan tokens and build argument list
list arglist;
int ntok = 0;
char* buf = strdup(cmdline.c_str());
char* tok = strtok(buf, " \t");
while ( tok != 0 )
{
arglist.push_back(tok);
ntok++;
// cout << "\"" << tok << "\"" << endl;
tok = strtok(0," \t");
}
// cout << " total of " << ntok << " tokens" << endl;
// arglist.dump();
// build ac and av
int ac = ntok;
char **av = new char *[ntok+1];
int i = 0;
list::iterator iarg = arglist.begin();
while ( iarg != arglist.end() )
{
av[i++] = *iarg++;
}
av[ntok] = 0;
// write arguments
// for ( i = 0; i < ac; i++ )
// {
// cout << av[i] << endl;
// }
// search cmdlist for command
tok = av[0];
// check for empty command line
if ( tok != 0 )
{
Cmd *cmdptr = findCmd(tok);
if ( cmdptr )
{
MPI_Barrier(MPI_COMM_WORLD);
#if DEBUG
cout << " execute command " << cmdptr->name() << endl;
#endif
cmdptr->action(ac,av);
MPI_Barrier(MPI_COMM_WORLD);
#if DEBUG
cout << " command completed " << cmdptr->name() << endl;
#endif
}
else
{
// command is not in the command list, check for script files
ifstream cmdstr;
int status;
if ( onpe0_ )
{
cmdstr.open(av[0],ios::in);
status = !cmdstr;
}
MPI_Bcast(&status,1,MPI_INT,0,MPI_COMM_WORLD);
if ( !status )
{
// create new prompt in the form: prompt
string newprompt;
if ( onpe0_ )
{
newprompt = prompt + "[" + av[0] + "]";
}
// MPI: process commands on all processes.
// Note: newprompt is 0 on all processes > 0
// Note: echo == true for scripts
processCmds(cmdstr, newprompt, true);
}
else
{
if ( onpe0_ )
cout << " No such command or file name: " << tok << endl;
}
}
}
delete [] av;
if ( onpe0_ )
cout << prompt << " ";
free(buf);
}
}