UserInterface.C 9.91 KB
Newer Older
Francois Gygi committed
1 2
////////////////////////////////////////////////////////////////////////////////
//
Francois Gygi committed
3 4 5 6
// Copyright (c) 2008 The Regents of the University of California
//
// This file is part of Qbox
//
Francois Gygi committed
7 8
// Qbox is distributed under the terms of the GNU General Public License
// as published by the Free Software Foundation, either version 2 of
Francois Gygi committed
9 10 11 12 13 14
// the License, or (at your option) any later version.
// See the file COPYING in the root directory of this distribution
// or <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////
//
Francois Gygi committed
15 16 17 18 19
// UserInterface.C: definition of readCmd and processCmds
//
////////////////////////////////////////////////////////////////////////////////

#include "UserInterface.h"
20
#include "qbox_xmlns.h"
Francois Gygi committed
21 22 23
#include <string>
#include <list>
#include <fstream>
24 25
#include <sstream>
#include <unistd.h> // fsync()
26
#include <cstdio> // fopen(), fclose(), fprintf()
27
#include <sys/types.h>
28
#include <sys/stat.h> // stat()
Francois Gygi committed
29
#include <mpi.h>
30 31 32
using namespace std;

////////////////////////////////////////////////////////////////////////////////
33
void wait_for_no_file(const string& lockfilename)
34
{
35
  //cerr << " waiting for no " << lockfilename << endl;
36 37 38 39 40
  struct stat statbuf;
  int status;
  do
  {
    // stat returns 0 if the file exists
41 42 43
    status = stat(lockfilename.c_str(),&statbuf);
    usleep(100000);
    // cerr << ".";
44 45 46
  }
  while ( status == 0 );
}
Francois Gygi committed
47 48 49 50

////////////////////////////////////////////////////////////////////////////////
UserInterface::UserInterface(void) : terminate_(false)
{
51
  int mype = 0;
Francois Gygi committed
52 53 54 55
  MPI_Comm_rank(MPI_COMM_WORLD,&mype);
  onpe0_ = ( mype == 0 );
}

Francois Gygi committed
56
////////////////////////////////////////////////////////////////////////////////
Francois Gygi committed
57
UserInterface::~UserInterface(void)
Francois Gygi committed
58 59 60 61 62 63 64 65 66
{
  std::list<Cmd*>::iterator cmd;
  for ( cmd = cmdlist.begin(); cmd != cmdlist.end(); cmd++ )
    delete *cmd;
  std::list<Var*>::iterator var;
  for ( var = varlist.begin(); var != varlist.end(); var++ )
    delete *var;
}

Francois Gygi committed
67
////////////////////////////////////////////////////////////////////////////////
68
int UserInterface::readCmd(string& s, istream &is, bool echo)
Francois Gygi committed
69
{
70 71 72 73 74 75 76
  getline(is,s);

  if ( is.eof() )
    return 0;

  // process line continuation if last character is '\'
  while ( (s.size() > 0) && (s[s.size()-1] == '\\') )
77
  {
78 79 80 81
    s[s.size()-1] = ' ';
    string stmp;
    getline(is,stmp);
    s += stmp;
Francois Gygi committed
82
  }
83

84 85
  if ( echo )
    cout << "<cmd>" << s << "</cmd>" << endl;
Francois Gygi committed
86

87 88 89 90 91 92 93
  // 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
94

95 96 97 98 99 100 101 102 103
  // 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
104

105 106 107
  // remove trailing white space
  pos = s.find_last_not_of(" \t");
  if ( pos != string::npos )
Francois Gygi committed
108
  {
109 110
    pos++;
    s = s.substr(0,pos);
Francois Gygi committed
111
  }
112 113 114
#ifdef DEBUG
  cout << "readCmd: after removing trailing space: @" << s << "@" << endl;
#endif
115

116
  return 1; // a command was read into s
Francois Gygi committed
117 118 119
}

////////////////////////////////////////////////////////////////////////////////
120
void UserInterface::processCmds(istream &cmdstream, const string prompt,
121
  bool echo)
Francois Gygi committed
122
{
123 124 125 126
#if DEBUG
  cout << "UserInterface::processCmds: prompt="
       << prompt << " echo=" << echo << endl;
#endif
127
  // read and process commands from cmdstream until done
128
  string cmdline;
Francois Gygi committed
129
  list<Cmd*>::iterator cmd;
130
  int done = 0, cmd_read = 0;
131

Francois Gygi committed
132
  if ( onpe0_ )
133
    cout << prompt << " ";
134

135 136 137 138 139
  while ( !done )
  {
    if ( onpe0_ )
    {
      // readCmd returns 1 if a command is read, 0 if at EOF
140
      cmd_read = readCmd(cmdline, cmdstream, echo );
141 142
      done = !cmd_read;
    }
143

144 145 146 147
    MPI_Bcast(&cmd_read,1,MPI_INT,0,MPI_COMM_WORLD);

    if ( cmd_read )
    {
148 149 150 151 152 153 154 155
      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;
156

157
      execCmd(cmdline,prompt);
158 159 160 161
    }

    if ( onpe0_ )
      done |= terminate_;
162

163 164 165 166 167 168 169 170 171
    MPI_Bcast(&done,1,MPI_INT,0,MPI_COMM_WORLD);
  }

  if ( onpe0_ )
    cout << " End of command stream " << endl;
}

////////////////////////////////////////////////////////////////////////////////
void UserInterface::processCmdsServer ( string inputfilename,
172
  string outputfilename, const string prompt, bool echo)
173 174 175 176 177
{
  // process commands in server mode
  // read commands from inputfilename
  // redirect cout to outputfilename

178
  string cmdline;
179
  list<Cmd*>::iterator cmd;
180
  int done = 0, cmd_read = 0;
181

182
  string lockfilename = inputfilename + ".lock";
183 184 185 186

  // in server mode, redirect output to stream qbout
  streambuf *cout_buf;
  ifstream qbin;
187
  ostringstream os;
188

Francois Gygi committed
189 190
  while ( !done )
  {
191
    if ( onpe0_ )
Francois Gygi committed
192
    {
193
      // create file to signal that Qbox is waiting for a command on qbin
194 195 196 197
      FILE *lockfile = fopen(lockfilename.c_str(),"w");
      fprintf(lockfile,"1");
      fclose(lockfile);
      fsync(fileno(lockfile));
198
      usleep(100000);
199
      wait_for_no_file(lockfilename.c_str());
200 201 202 203

      qbin.open(inputfilename.c_str());
      qbin.sync();
      qbin.clear();
204

205 206 207
      // clear os
      os.str("");

208 209
      // save copy of cout streambuf
      cout_buf = cout.rdbuf();
210 211 212
      // redirect cout to os
      cout.rdbuf(os.rdbuf());
      // cerr << " processCmdsServer: cout streambuf redirected" << endl;
213 214 215

      cout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
      cout << "<fpmd:simulation xmlns:fpmd=\"" << qbox_xmlns() << "\">" << endl;
Francois Gygi committed
216
    }
217

218
    do
Francois Gygi committed
219 220 221
    {
      if ( onpe0_ )
      {
222
        // readCmd returns 1 if a command is read, 0 if at EOF
223
        cmd_read = readCmd(cmdline, qbin, echo);
224
        cout << prompt << " " << cmdline << endl;
Francois Gygi committed
225
      }
226

227
      MPI_Bcast(&cmd_read,1,MPI_INT,0,MPI_COMM_WORLD);
Francois Gygi committed
228

229
      if ( cmd_read )
Francois Gygi committed
230
      {
231 232 233
        int n = cmdline.size();
        MPI_Bcast(&n,1,MPI_INT,0,MPI_COMM_WORLD);
        char* buf = new char[n];
234
        if ( onpe0_ )
235 236 237 238
          strncpy(buf,cmdline.c_str(),n);
        MPI_Bcast(buf,n,MPI_CHAR,0,MPI_COMM_WORLD);
        cmdline = string(buf,n);
        delete [] buf;
239

240 241
        execCmd(cmdline,prompt);
      }
242

243 244
      if ( onpe0_ )
        done |= terminate_;
245

246
      MPI_Bcast(&done,1,MPI_INT,0,MPI_COMM_WORLD);
247 248 249 250

    } while ( !done && cmd_read );

    if ( onpe0_ )
Francois Gygi committed
251
    {
252 253 254 255
      qbin.close();
      cout << " End of command stream " << endl;
      cout << "</fpmd:simulation>" << endl;
      cout.flush();
256 257 258 259 260 261 262 263

      // 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
264
      cout.rdbuf(cout_buf);
265
      // cerr << " processCmdsServer: cout streambuf reassigned" << endl;
Francois Gygi committed
266 267
    }

268 269
    // wait before retrying
    usleep(200000);
270

271
  } // while !done
272 273 274

  if ( onpe0_ )
  {
275 276
    // remove lock file
    remove(lockfilename.c_str());
277
  }
Francois Gygi committed
278
}
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402

////////////////////////////////////////////////////////////////////////////////
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<char*> 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<char*>::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<filename>
          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);
  }
}