$search
00001 #include <utilmm/system/process.hh> 00002 #include <utilmm/system/system.hh> 00003 00004 #include "boost/thread/mutex.hpp" 00005 00006 #include <sys/types.h> 00007 #include <sys/wait.h> 00008 #include <signal.h> 00009 #include <unistd.h> 00010 #include <fcntl.h> 00011 #include <errno.h> 00012 #include <stdlib.h> 00013 #include <cstring> 00014 #include <stdio.h> 00015 00016 #include <iostream> 00017 #include <boost/version.hpp> 00018 #include <boost/program_options.hpp> 00019 #include <boost/filesystem/exception.hpp> 00020 #include <boost/filesystem/operations.hpp> 00021 #include <boost/filesystem/path.hpp> 00022 00023 using std::list; 00024 using std::string; 00025 using std::cout; 00026 using std::cerr; 00027 using std::endl; 00028 00029 using namespace boost::filesystem; 00030 00031 00032 namespace utilmm 00033 { 00034 bool process::output_file::is_null() const 00035 { 00036 try { handle<int>(); } 00037 catch(std::bad_cast) { return true; } 00038 return false; 00039 } 00040 00041 void process::output_file::redirect(FILE* stream) 00042 { 00043 if (is_null()) 00044 return; 00045 00046 if (dup2(handle<int>(), fileno(stream)) < 0) 00047 throw unix_error(); 00048 } 00049 } 00050 00051 namespace 00052 { 00053 char* const* stringlist_to_execlist(list<string>& strlist) 00054 { 00055 char** ret = new char* [ strlist.size() + 1 ]; 00056 int i = 0; 00057 for (list<string>::iterator it = strlist.begin(); it != strlist.end(); ++it) 00058 ret[i++] = const_cast<char*>(it->c_str()); 00059 ret[ i ] = 0; 00060 return ret; 00061 } 00062 00063 using utilmm::process; 00064 using boost::mutex; 00065 00066 boost::mutex mtx_processes; 00067 typedef std::list<process*> ProcessList; 00068 typedef ProcessList::iterator ProcessHandle; 00069 std::list<process*> managed_processes; 00070 00071 void register_process(process* process) 00072 { 00073 sigset_t set, old_set; 00074 sigemptyset(&set); 00075 sigaddset(&set, SIGINT); 00076 00077 sigprocmask(SIG_BLOCK, &set, &old_set); 00078 managed_processes.push_back(process); 00079 sigprocmask(SIG_SETMASK, &old_set, 0); 00080 } 00081 00082 void deregister_process(process* process) 00083 { 00084 sigset_t set, old_set; 00085 sigemptyset(&set); 00086 sigaddset(&set, SIGINT); 00087 00088 sigprocmask(SIG_BLOCK, &set, &old_set); 00089 managed_processes.remove(process); 00090 sigprocmask(SIG_SETMASK, &old_set, 0); 00091 } 00092 00093 RETSIGTYPE (*old_sigint_handler)(int) = 0; 00094 RETSIGTYPE sigint_handler(int signum) 00095 { 00096 if (old_sigint_handler) 00097 old_sigint_handler(signum); 00098 else 00099 abort(); 00100 } 00101 00102 } 00103 00104 using namespace utilmm; 00105 00106 process::process() 00107 : m_running(false), m_pid(0), m_normalexit(true), m_status(0), m_do_setpgid(false) 00108 { 00109 register_process(this); 00110 } 00111 process::~process() 00112 { 00113 deregister_process(this); 00114 if (m_running) 00115 { 00116 signal(); 00117 wait(true); 00118 } 00119 } 00120 00121 boost::filesystem::path process::workdir() const { return m_wdir; } 00122 void process::set_workdir(boost::filesystem::path const& wdir) 00123 { m_wdir = wdir; } 00124 00125 list<string> process::cmdline() const { return m_cmdline; } 00126 void process::push(const std::string& argument) { m_cmdline.push_back(argument); } 00127 process& process::operator << (std::string const& newarg) 00128 { 00129 push(newarg); 00130 return *this; 00131 } 00132 void process::clear() { m_cmdline.clear(); } 00133 00134 00135 00136 void process::install_sigint_handler() 00137 { 00138 static bool setup = false; 00139 if (setup) 00140 return; 00141 00142 struct sigaction action; 00143 memset(&action, 0, sizeof(struct sigaction)); 00144 action.sa_handler = sigint_handler; 00145 00146 struct sigaction old_action; 00147 sigaction(SIGINT, &action, &old_action); 00148 if (old_action.sa_flags & SA_SIGINFO) 00149 std::cerr << "WARNING: overriding previous SIGINT signal handler" << std::endl; 00150 else if (old_action.sa_handler != SIG_IGN && old_action.sa_handler != SIG_DFL) 00151 old_sigint_handler = old_action.sa_handler; 00152 00153 setup = true; 00154 } 00155 00156 void process::killall() 00157 { 00158 for (ProcessList::iterator it = managed_processes.begin(); it != managed_processes.end(); ++it) 00159 kill((*it)->pid(), SIGINT); 00160 } 00161 void process::erase_redirection(Stream stream) { redirect_to(stream, ""); } 00162 void process::redirect_to( Stream stream, boost::filesystem::path const& file) 00163 { 00164 #if BOOST_VERSION >= 104600 00165 int handle = open(file.string().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); 00166 #else 00167 int handle = open(file.native_file_string().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); 00168 #endif 00169 if (handle == -1) 00170 throw unix_error(); 00171 00172 redirect_to(stream, handle, true); 00173 } 00174 00175 void process::redirect_to( Stream stream, FILE* handle, bool close_on_exit) 00176 { 00177 redirect_to(stream, dup(fileno(handle)), close_on_exit); 00178 if (close_on_exit) 00179 fclose(handle); 00180 } 00181 00182 void process::redirect_to( Stream stream, int handle, bool close_on_exit) 00183 { 00184 if (!close_on_exit) 00185 { 00186 handle = dup(handle); 00187 if (handle == -1) 00188 throw unix_error(); 00189 } 00190 get_stream(stream).reset(handle); 00191 } 00192 00193 process::output_file& process::get_stream(Stream stream) 00194 { 00195 switch(stream) 00196 { 00197 case Stdout: return m_stdout; 00198 case Stderr: return m_stderr; 00199 } 00200 00201 // Never reached 00202 assert(false); 00203 return m_stdout; 00204 } 00205 00206 void process::set_pgid(pid_t pgid) 00207 { 00208 m_do_setpgid = true; 00209 m_pgid = pgid; 00210 } 00211 00212 static const int CHDIR_ERROR = 0; 00213 static const int EXEC_ERROR = 1; 00214 void process::start() 00215 { 00216 if (running()) 00217 throw already_running(); 00218 00219 list<string> argv = m_cmdline; 00220 Env env = m_env; 00221 00222 int pc_comm[2]; 00223 pipe(pc_comm); 00224 auto_close read_guard(pc_comm[0]); 00225 auto_close write_guard(pc_comm[1]); 00226 00227 pid_t child_pid = fork(); 00228 if (child_pid == -1) 00229 throw unix_error(errno); 00230 else if (child_pid) 00231 { 00232 // close fds on the parent side 00233 m_stdout.close(); 00234 m_stderr.close(); 00235 write_guard.close(); 00236 00237 // wait for the exec() to happen 00238 process_child_error(pc_comm[0]); 00239 00240 m_pid = child_pid; 00241 m_running = true; 00242 return; 00243 } 00244 else 00245 { 00246 // in the child 00247 char* const* exec_argv = stringlist_to_execlist(argv); 00248 char* prog = exec_argv[0]; 00249 00250 m_stdout.redirect(stdout); 00251 m_stderr.redirect(stderr); 00252 read_guard.close(); 00253 00254 if (m_do_setpgid) 00255 setpgid(0, m_pgid); 00256 00257 // must use execvp bec. we want exec to search for prog for us 00258 // so, should set environment variables ourselves 00259 for (Env::const_iterator it = env.begin(); it != env.end(); ++it) 00260 { 00261 std::string putenv_arg = (it->first + "=" + it->second); 00262 putenv( strdup(putenv_arg.c_str()) ); 00263 } 00264 00265 if (!m_wdir.empty()) 00266 { 00267 #if BOOST_VERSION >= 104600 00268 if (chdir(m_wdir.string().c_str()) == -1) 00269 #else 00270 if (chdir(m_wdir.native_file_string().c_str()) == -1) 00271 #endif 00272 send_child_error(pc_comm[1], CHDIR_ERROR); 00273 } 00274 00275 fcntl(pc_comm[1], F_SETFD, FD_CLOEXEC); 00276 execvp(prog, exec_argv); 00277 00278 // Error if reached 00279 send_child_error(pc_comm[1], EXEC_ERROR); 00280 } 00281 } 00282 00283 void process::process_child_error(int fd) 00284 { 00285 int error_type; 00286 int error; 00287 if (read(fd, &error_type, sizeof(error_type)) == 0) 00288 return; 00289 read(fd, &error, sizeof(error)); 00290 00291 throw unix_error(error); 00292 } 00293 00294 void process::send_child_error(int fd, int error_type) 00295 { 00296 int error = errno; 00297 write(fd, &error_type, sizeof(error_type)); 00298 write(fd, &error, sizeof(error)); 00299 exit(1); 00300 } 00301 00302 void process::detach() 00303 { 00304 m_running = false; 00305 m_pid = 0; 00306 } 00307 void process::signal(int signo) 00308 { 00309 if (!running()) 00310 return; 00311 00312 if (::kill(m_pid, signo) == 0) 00313 return; 00314 00315 if (errno != ESRCH) 00316 throw unix_error(); 00317 } 00318 00319 void process::wait() { wait(true); } 00320 bool process::wait(bool hang) 00321 { 00322 int status; 00323 00324 pid_t wait_ret = -1; 00325 do 00326 { wait_ret = waitpid(m_pid, &status, (hang ? 0 : WNOHANG) ); } 00327 while (wait_ret == -1 && errno == EINTR); 00328 00329 if (!hang && wait_ret == 0) 00330 return false; 00331 00332 // We consider that ECHILD means the process has terminated 00333 // EINTR is taken care of 00334 // EINVAL is an internal error 00335 00336 m_running = false; 00337 00338 if (wait_ret != -1) // no status information if wait_ret == -1 00339 { 00340 m_normalexit = WIFEXITED(status); 00341 if (m_normalexit) 00342 m_status = WEXITSTATUS(status); 00343 else m_status = 0; 00344 } 00345 return true; 00346 } 00347 00348 bool process::exit_normal() const { return m_normalexit; } 00349 int process::exit_status() const { return m_status; } 00350 bool process::running() 00351 { 00352 if (! m_running) 00353 return false; 00354 00355 wait(false); 00356 return m_running; 00357 } 00358 pid_t process::pid() const { return m_pid; } 00359 00360 void process::set_environment(const std::string& key, const std::string& value) 00361 { m_env[key] = value; } 00362 std::string process::environment(const std::string& key) const 00363 { 00364 Env::const_iterator it = m_env.find(key); 00365 if (it == m_env.end()) return string(); 00366 return it->second; 00367 } 00368 void process::clear_environment() { m_env.clear(); } 00369