////////////////////////////////////////////////////////////////////////////////
//
// 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 // sync()
#include
#include
#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(char *s, int max, istream &fp, bool echo)
{
int ch, i = 0;
while ( (ch = fp.get()) != EOF && !( ch == '\n' || ch ==';' || ch == '#') )
{
if ( ch == '\\' ) // line continuation character
{
// check if backslash is followed by a newline
ch = fp.get();
if ( ch == '\n' )
{
// backslash followed by newline, do nothing
}
else
{
// backslash not followed by newline
if ( i < max - 1 )
s[i++] = '\\';
if ( i < max - 1 )
s[i++] = ch;
}
}
if (i < max - 1)
s[i++] = ch;
}
if (max > 0) s[i] = '\0'; /* add terminating NULL */
if ( fp.eof() )
return 0; /* return 0 for end of file */
// output command line if reading from a script
if ( echo && i > 0 ) cout << "" << s << "" << endl;
if ( ch == '#' )
{
if ( echo ) cout << "#";
while ( (ch = fp.get()) != EOF && !( ch == '\n' ) )
{
if ( echo ) cout << (char) ch;
}
if ( echo && ch=='\n' ) cout << "" << endl;
if ( !(ch == '\n') )
return 0; /* return 0 for end of file */
}
return 1; // a command was read
}
////////////////////////////////////////////////////////////////////////////////
void UserInterface::processCmds ( istream &cmdstream, const char *prompt,
bool echo)
{
#if DEBUG
cout << "UserInterface::processCmds: prompt="
<< prompt << " echo=" << echo << endl;
#endif
// read and process commands from cmdstream until done
const int cmdlinemax = 1024;
char cmdline[cmdlinemax];
list::iterator cmd;
const char *separators = " ;\t";
int done=0,cmd_read,status;
if ( onpe0_ )
cout << prompt << " ";
while ( !done )
{
if ( onpe0_ )
{
for ( int i = 0; i < cmdlinemax; i++ )
cmdline[i] = '\0';
// readCmd returns 1 if a command is read, 0 if at EOF
cmd_read = readCmd(cmdline, cmdlinemax, cmdstream, echo );
done = !cmd_read;
}
MPI_Bcast(&cmdline[0],cmdlinemax,MPI_CHAR,0,MPI_COMM_WORLD);
MPI_Bcast(&cmd_read,1,MPI_INT,0,MPI_COMM_WORLD);
if ( cmd_read )
{
// cmdline contains a string of tokens terminated by '\0'
// cout << " command line is: " << cmdline << endl;
// comment lines: start with '#'
int i;
if ( cmdline[i=strspn(cmdline," ")] == '#' )
{
// cout << " comment line" << endl;
// do nothing, not even write prompt
}
else if ( cmdline[i=strspn(cmdline," ")] == '!' )
{
// shell escape commands start with '!'
// cout << " shell escape" << endl;
if ( onpe0_ )
{
system ( &cmdline[i+1] );
cout << prompt << " ";
}
}
else
{
// cout << " command split in the following tokens:" << endl;
// scan tokens and build argument list
list arglist;
int ntok = 0;
char* tok = strtok(cmdline, separators);
while ( tok != 0 )
{
arglist.push_back(tok);
ntok++;
// cout << "\"" << tok << "\"" << endl;
tok = strtok(0,separators);
}
// cout << " total of " << ntok << " tokens" << endl;
// arglist.dump();
// build ac and av
int ac = ntok;
char **av = new char *[ntok+1];
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;
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
char *newprompt=0;
if ( onpe0_ )
{
newprompt = new char[strlen(prompt)+strlen(av[0])+4];
newprompt = strcpy(newprompt,prompt);
newprompt = strcat(newprompt,"[");
newprompt = strcat(newprompt,av[0]);
newprompt = strcat(newprompt,"]");
}
// MPI: process commands on all processes.
// Note: newprompt is 0 on all processes > 0
// Note: echo == true for scripts
processCmds (cmdstr, newprompt, true);
if ( onpe0_ )
delete newprompt;
}
else
{
if ( onpe0_ )
cout << " No such command or file name: " << tok << endl;
}
}
}
delete [] av;
if ( onpe0_ )
cout << prompt << " ";
}
}
else // if cmd_read
{
// found eof
done = true;
}
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 char *prompt, bool echo)
{
// process commands in server mode
// read commands from inputfilename
// redirect cout to outputfilename
char cmdline[256];
list::iterator cmd;
const char *separators = " ;\t";
int i,done=0,cmd_read,status;
string lockfilename = inputfilename + ".lock";
// in server mode, redirect output to stream qbout
streambuf *qbout_buf;
streambuf *cout_buf;
ofstream qbout;
ifstream qbin;
ofstream tstfile;
while ( !done )
{
if ( onpe0_ )
{
// create file to signal that Qbox is waiting for a command on qbin
tstfile.open(lockfilename.c_str());
tstfile << "1" << endl;
tstfile.close();
sync();
// wait for tstfile to be removed by the driver
usleep(100000);
wait_for_no_file(lockfilename.c_str());
qbin.open(inputfilename.c_str());
qbin.sync();
qbin.clear();
// save copy of cout streambuf
cout_buf = cout.rdbuf();
qbout.open(outputfilename.c_str(),ios_base::trunc);
qbout_buf = qbout.rdbuf();
// redirect cout
cout.rdbuf(qbout_buf);
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, 256, qbin, echo );
cout << prompt << " " << cmdline << endl;
}
MPI_Bcast(&cmdline[0],256,MPI_CHAR,0,MPI_COMM_WORLD);
MPI_Bcast(&cmd_read,1,MPI_INT,0,MPI_COMM_WORLD);
if ( cmd_read )
{
if ( onpe0_ )
{
cerr << " read cmd: " << cmdline << endl;
cerr << " executing ... ";
}
// cmdline contains a string of tokens terminated by '\0'
// cout << " command line is: " << cmdline << endl;
// comment lines: start with '#'
if ( cmdline[i=strspn(cmdline," ")] == '#' )
{
// cout << " comment line" << endl;
// do nothing, not even write prompt
}
else if ( cmdline[i=strspn(cmdline," ")] == '!' )
{
// shell escape commands start with '!'
// cout << " shell escape" << endl;
if ( onpe0_ )
{
system ( &cmdline[i+1] );
// cout << prompt << " ";
}
}
else
{
// cout << " command split in the following tokens:" << endl;
// scan tokens and build argument list
list arglist;
int ntok = 0;
char* tok = strtok(cmdline, separators);
while ( tok != 0 )
{
arglist.push_back(tok);
ntok++;
// cout << "\"" << tok << "\"" << endl;
tok = strtok(0,separators);
}
// cout << " total of " << ntok << " tokens" << endl;
// arglist.dump();
// build ac and av
int ac = ntok;
char **av = new char *[ntok+1];
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
cerr << " execute command " << cmdptr->name() << endl;
#endif
cmdptr->action(ac,av);
MPI_Barrier(MPI_COMM_WORLD);
#if DEBUG
cerr << " command completed " << cmdptr->name() << endl;
#endif
}
else
{
// command is not in the command list, check for script files
ifstream cmdstr;
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
char *newprompt=0;
if ( onpe0_ )
{
newprompt = new char[strlen(prompt)+strlen(av[0])+4];
newprompt = strcpy(newprompt,prompt);
newprompt = strcat(newprompt,"[");
newprompt = strcat(newprompt,av[0]);
newprompt = strcat(newprompt,"]");
}
// MPI: process commands on all processes.
// Note: newprompt is 0 on all processes > 0
// Note: echo == true for scripts
processCmds (cmdstr, newprompt, true);
if ( onpe0_ )
delete newprompt;
}
else
{
if ( onpe0_ )
cout << " No such command or file name: " << tok << endl;
}
}
}
delete [] av;
}
if ( onpe0_ )
cerr << "done" << endl;
// check if terminate_ flag was set during command execution
if ( onpe0_ )
done = terminate_;
MPI_Bcast(&done,1,MPI_INT,0,MPI_COMM_WORLD);
} // if cmd_read
} while ( !done && cmd_read );
if ( onpe0_ )
{
qbin.close();
cout << " End of command stream " << endl;
cout << "" << endl;
cout.flush();
qbout.close();
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());
sync();
}
}