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
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
00233 m_stdout.close();
00234 m_stderr.close();
00235 write_guard.close();
00236
00237
00238 process_child_error(pc_comm[0]);
00239
00240 m_pid = child_pid;
00241 m_running = true;
00242 return;
00243 }
00244 else
00245 {
00246
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
00258
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
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
00333
00334
00335
00336 m_running = false;
00337
00338 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