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