Logo Search packages:      
Sourcecode: passepartout version File versions  Download package

processman.cc

///
// Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING
///
#include "processman.h"
#include "filedescriptors.h"
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include "warning.h"
#include "os.h"
#include "filesys.h"
#include <stdexcept>
#include <cassert>

extern char **environ;


ProcessRecord::ProcessRecord(const std::string& line)
  : command_line(line)
{
  int ofiledes[2], ifiledes[2];
  // output from the external process (input to us)
  if(pipe(ofiledes) == -1)
    throw ClibException("Failed to create output pipe");
  
  // input to the external process (output from us)
  if(pipe(ifiledes) == -1)
    throw ClibException("Failed to create input pipe");
  

  pid = fork();
  if(pid == -1)
    throw ClibException("Failed to fork");
  
  if(pid == 0) {
    char *tmp = new char[command_line.length() + 1];
    command_line.copy(tmp, std::string::npos); 
    tmp[command_line.length()] = 0;
    /// \todo  We shouldn't really need an extra sh (it's just for managing io
    // redirects, which we should get rid of anyway) ...
    char *argv[4] = {"sh", "-c", tmp, 0};

    assert(filedesc::move(STDIN_FILENO, ifiledes[0]) == 0);
    assert(filedesc::move(STDOUT_FILENO, ofiledes[1]) == 0);
    close(ifiledes[1]); close(ofiledes[0]);
    /// \todo  Close all file descriptors above 2!
    execve("/bin/sh", argv, environ);
    delete[] tmp;
    exit(127);
  }
  close(ifiledes[0]); close(ofiledes[1]);
  inbuf.reset(new filedesc::OutBuf(ifiledes[1], true));
  outbuf.reset(new filedesc::InBuf(ofiledes[0], true));
}

ProcessRecord::ProcessRecord(const std::vector<std::string>& args) {
  int ofiledes[2], ifiledes[2];
  // output from the external process (input to us)
  if(pipe(ofiledes) == -1)
    throw ClibException("Failed to create output pipe");
  
  // input to the external process (output from us)
  if(pipe(ifiledes) == -1)
    throw ClibException("Failed to create input pipe");
  
  pid = fork();
  if(pid == -1)
    throw ClibException("Failed to fork");
  
  if(pid == 0) {
    // Convert the argument list so the c interface can use it.
    const char** argv = new const char*[args.size()+1];
    for(int i = 0; i < args.size(); ++i) {
      argv[i] = args[i].c_str();
    }
    argv[args.size()] = 0;
    
    // Set up stdio
    assert(filedesc::move(STDIN_FILENO, ifiledes[0]) == 0);
    assert(filedesc::move(STDOUT_FILENO, ofiledes[1]) == 0);
    close(ifiledes[1]); close(ofiledes[0]);
    /// \todo  Close all file descriptors above 2!
    
    execvp(const_cast<char*>(argv[0]), const_cast<char**>(argv));
    delete[] argv;
    exit(127);
  }
  close(ifiledes[0]); close(ofiledes[1]);
  inbuf.reset(new filedesc::OutBuf(ifiledes[1], true));
  outbuf.reset(new filedesc::InBuf(ofiledes[0], true));
}

int ProcessRecord::wait() {
  int status;
  if(waitpid(get_pid(), &status, 0) == -1 || !WIFEXITED(status))
    return -1; // no process or abnormal termination
  else
    return WEXITSTATUS(status); // return exit status
}

std::ostream& ProcessRecord::get_cin() {
  if(!inbuf.get())
    throw std::runtime_error("Tried to get closed process stream");
  
  if(!in.get())
    in.reset(new std::ostream(inbuf.get()));
  return *in;
}
std::istream& ProcessRecord::get_cout() {
  if(!out.get())
    out.reset(new std::istream(outbuf.get()));
  return *out;
}

void ProcessRecord::close_cin() {
  in.reset();
  inbuf.reset();
}

ProcessManager *ProcessManager::_instance = 0;

ProcessManager &ProcessManager::instance() {
  if(!_instance)
    _instance = new ProcessManager(); 
  return *_instance;
}

ProcessManager::ProcessManager()
{
  // Get a callback to run_check every 100ms.
  Glib::signal_timeout().connect
    (slot(*this, &ProcessManager::run_check), 100);
}

ProcessManager::~ProcessManager() {
  stop_all();
}

Process ProcessManager::run(const std::string& command) {
  Process result(new ProcessRecord(command));
  result->reference();
  processes[result->get_pid()] = result;
  return result;
}

Process ProcessManager::run(const std::vector<std::string>& command) {
  Process result(new ProcessRecord(command));
  result->reference();
  processes[result->get_pid()] = result;
  return result;
}

int ProcessManager::system(std::vector<std::string>& command) {
  Process proc(new ProcessRecord(command));
  proc->reference();
  return proc->wait();
}

int ProcessManager::system(std::string command) {
  int status; 
  pid_t pid = fork();
  if(pid == -1)
    return -1;
  if(pid == 0) {
    char *tmp = new char[command.length() + 1];
    command.copy(tmp, std::string::npos); tmp[command.length()] = 0;
    char *argv[4] = {"sh", "-c", tmp, 0};
    execve("/bin/sh", argv, environ);
    delete[] tmp;
    exit(127);
  }
  if(waitpid(pid, &status, 0) == -1 || !WIFEXITED(status))
    return -1; // no process or abnormal termination
  else
    return WEXITSTATUS(status); // return exit status
}

bool ProcessManager::stop_all() {
  bool tmp = true; 
  while(!processes.empty())
    tmp = tmp && stop(processes.begin()->first);
  return tmp;
}

bool ProcessManager::stop(pid_t pid) {
  Processes::iterator i = processes.find(pid);
  if(i != processes.end()) {
    bool tmp = kill(i->first, 9) == 0;
    debug << i->first << " killed" << std::endl;
    processes.erase(i);
    return tmp;
  }
  else return false;
}

Process *ProcessManager::get_process(pid_t pid) {
  Processes::iterator i = processes.find(pid);
  if(i != processes.end())
    return &(i->second);
  return 0;
}

bool ProcessManager::run_check() {
  pid_t tmp;
  int status;
  while((tmp = waitpid(-1, &status, WNOHANG)) != 0 && tmp != -1) {
    const Processes::iterator i = processes.find(tmp);
    if(i != processes.end()) {
      debug << tmp << " done" << std::endl;
      processes.erase(i);
      process_stopped(tmp, WIFEXITED(status), 
                  WIFEXITED(status) ? WEXITSTATUS(status) : 0);
    }
  }
  return true;
}

Generated by  Doxygen 1.6.0   Back to index