process.cc
Go to the documentation of this file.
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 


utilmm
Author(s): Sylvain Joyeux/sylvain.joyeux@m4x.org
autogenerated on Thu Jan 2 2014 11:38:31