/// // Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING /// #include "processman.h" #include "filedescriptors.h" #include #include #include #include "warning.h" #include "os.h" #include "filesys.h" #include #include 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& 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(argv[0]), const_cast(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 (sigc::mem_fun(*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& command) { Process result(new ProcessRecord(command)); result->reference(); processes[result->get_pid()] = result; return result; } int ProcessManager::system(std::vector& 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; }