aa0fb9a18c1142ddd1258b9d44ff05e5e0c76f1e
[libasyncio] / asyncio / async_process.cpp
1 /** @file
2  *
3  *
4  * (c) Copyright 2007-2008 by Intra2net AG
5  *
6  * info@intra2net.com
7  */
8
9 //#define NOISEDEBUG
10
11 #include "async_process.hpp"
12
13 #include <iterator>
14 #include <algorithm>
15
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <sys/socket.h>
19 #include <sys/types.h>
20 #include <errno.h>
21 #include <signal.h>
22 #include <sys/wait.h>
23
24 #include <filefunc.hxx>
25
26
27 #ifdef NOISEDEBUG
28 #include <iostream>
29 #include <iomanip>
30 #define DOUT(msg) std::cout << msg << std::endl
31 #define FODOUT(obj,msg) std::cout << typeid(*obj).name() << "[" << obj << "]:" << msg << std::endl
32 #define ODOUT(msg) std::cout << typeid(*this).name() << "[" << this << "]:" << msg << std::endl
33 #else
34 #define DOUT(msg) do {} while (0)
35 #define FODOUT(obj,msg) do {} while (0)
36 #define ODOUT(msg) do {} while (0)
37 #endif
38
39
40 namespace
41 {
42
43 using namespace AsyncIo;
44
45 /**
46  * local configuration values
47  */
48 namespace config
49 {
50
51    /// the capacity of the child status list (/ vector)
52    const unsigned int pid_pool_capacity= 512;
53
54 } // eo namespace config
55
56
57
58 /// the previous handler for the child signal (SIGCHLD)
59 void (*oldChildHandler)(int) = NULL;
60
61 /// method pointer for activating process manager
62 void (ProcessManager::*_activate_manager)();
63
64 PidStateList pending_pid_states;
65
66
67 /**
68  * signal handler for child signal (SIGCHLD)
69  * @param sig the signal number as provided by the OS
70  */
71 void handleSigChild(int sig)
72 {
73    int status;
74    pid_t pid;
75    while ( (pid = waitpid(-1,&status,WNOHANG)) > 0)
76    {
77       pending_pid_states.push_back( PidStatePair(pid,status) );
78    }
79    if (_activate_manager)
80    {
81       // tricky way to access a protected method without being a (official) friend:
82       ( ProcessManager::getInstance()->*_activate_manager)();
83    }
84    //TODO: ?
85    signal(sig,handleSigChild);
86 } // eo handleSigChild
87
88
89 namespace process
90 {
91
92 typedef std::pair<pid_t, ProcessImplementation*> PidProcPair;
93 typedef std::list< PidProcPair > PidProcList;
94
95
96 template< typename F, typename S >
97 struct CmpFirst
98 {
99    F _f;
100    CmpFirst ( F f ) : _f(f) {}
101    bool operator () ( const std::pair<F,S>& v ) const { return v.first == _f; }
102 }; // eo struct CmpFirst
103
104
105 std::list<ProcessImplementation*> g_process_list;
106 PidProcList g_pid_list;
107
108
109 void addProcessInstance( ProcessImplementation* obj )
110 {
111    g_process_list.push_back(obj);
112 } // eo addProcessInstance(ProcessImplementation*)
113
114
115 void removeProcessInstance( ProcessImplementation* obj )
116 {
117    // remove obj from list
118    g_process_list.remove(obj);
119    // clear pointers in pid list
120    for(PidProcList::iterator it= g_pid_list.begin();
121       it != g_pid_list.end();
122       ++it)
123    {
124       if (it->second == obj)
125       {
126          it->second= NULL;
127       }
128    }
129 } // eo removeProcessInstance(ProcessImplementation*)
130
131
132 void addChildProcess( pid_t pid, ProcessImplementation* obj)
133 {
134    g_pid_list.push_back ( PidProcPair(pid,obj) );
135 } // eo addChildProcess(pid_t,ProcessImplementation*)
136
137
138 void removeChildProcess ( pid_t pid, ProcessImplementation* obj)
139 {
140    PidProcList::iterator it= std::find(
141       g_pid_list.begin(), g_pid_list.end(),
142       PidProcPair(pid,obj));
143    if (it != g_pid_list.end())
144    {
145       g_pid_list.erase(it);
146    }
147 } // eo removeChildProcess(pid_t,ProcessImplementation*)
148
149
150 bool findChildProcess ( pid_t pid, ProcessImplementation* & obj )
151 {
152    PidProcList::iterator it = std::find_if(
153       g_pid_list.begin(), g_pid_list.end(),
154       CmpFirst<pid_t,ProcessImplementation*>(pid) );
155    if (it == g_pid_list.end())
156    {
157       return false;
158    }
159    obj = it->second;
160    return true;
161 } // eo findChildProcess(pid_t,ProcessImplementation*&)
162
163
164 } // eo namespace process
165
166
167
168
169
170 /*
171 ** misc tools
172 */
173
174
175 /**
176  * convenience tool for closing file descriptors...
177  */
178 struct FdCloser
179 {
180    int m_fd;
181
182    FdCloser(int fd=-1) : m_fd(fd) {}
183
184    ~FdCloser()
185    {
186       if (m_fd >= 0) ::close(m_fd);
187    }
188
189    void release() { m_fd= -1; }
190
191 }; // eo struct FdCloser
192
193
194
195 } // eo namespace <anonymous>
196
197
198 namespace AsyncIo
199 {
200
201
202 /*
203  * global functions
204  */
205
206 /**
207  * installs the handler for the child signal (SIGCHLD).
208  * Installing this handler is mandatory for the process subsystem to work correctly.
209  * @return @a true iff the child handler is successfully installed.
210  */
211 bool installChildHandler()
212 {
213    if (oldChildHandler)
214    {
215       // already installed
216       return true;
217    }
218    if (! ProcessManager::getInstance() )
219    {
220       // we need an instance of the process manager
221       return false;
222    }
223    pending_pid_states.reserve( config::pid_pool_capacity );
224    oldChildHandler = signal( Signal::CHLD, handleSigChild );
225    if (oldChildHandler == SIG_ERR)
226    {
227       oldChildHandler= NULL;
228       return false;
229    }
230    return true;
231 } // eo installChildHandler
232
233
234 /**
235  * uninstalls the child handler.
236  * @return @a true iff the old child handler is reestablished.
237  */
238 bool restoreChildHandler()
239 {
240    if (!oldChildHandler)
241    {
242       return false;
243    }
244    void(*res)(int) = signal( Signal::CHLD, oldChildHandler);
245
246    if (res == SIG_ERR)
247    {
248       return false;
249    }
250    oldChildHandler= NULL;
251    return true;
252 } // eo restoreChildHandler
253
254
255
256
257 /*
258  * Implementation of ProcessImplementation
259  */
260
261 IOImplementation2* ProcessImplementation::_StderrOnStdout   = ((IOImplementation2*) 1);
262 IOImplementation2* ProcessImplementation::_UseParentsStderr = ((IOImplementation2*) 0);
263
264
265 /**
266  * @brief constructor for the process implementation.
267  *
268  * the constructor takes the path to the executable and (initial) cli arguments.
269  *
270  * @param path path to the executable.
271  * @param args initial command line arguments.
272  */
273 ProcessImplementation::ProcessImplementation(
274    const std::string& path,
275    const std::vector<std::string>& args
276    )
277 : IOImplementation(-1,-1)
278 , m_path(path)
279 , m_nice_inc(0)
280 , m_create_new_session(false)
281 , m_pid(0)
282 , m_state(ProcessState::stopped)
283 , m_exit_code(0)
284 {
285    m_args.push_back(path);
286    std::copy( args.begin(), args.end(), std::back_inserter(m_args) );
287    process::addProcessInstance(this);
288 } // eo ProcessImplementation::ProcessImplementation(const std::string&)
289
290
291 ProcessImplementation::~ProcessImplementation()
292 {
293    if (m_pid > 0 && m_state!=ProcessState::stopped)
294    {
295       stopProcess(true);
296    }
297    process::removeProcessInstance(this);
298 } // eo ProcessImplementation::~ProcessImplementation()
299
300
301 void ProcessImplementation::close(Direction direction)
302 {
303    inherited::close(direction);
304    if (!inherited::opened() &&  (m_state != ProcessState::stopped) )
305    {
306       stopProcess(false);
307    }
308 } // eo ProcessImplementation::close(Direction)
309
310
311 /**
312  * returns an object for adding new arguments to the argument list.
313  * @return the adder object.
314  */
315 PushBackFiller<std::string, std::vector > ProcessImplementation::getArgAdder()
316 {
317    return PushBackFiller<std::string, std::vector >(m_args);
318 } // eo ProcessImplementation::getArgAdder()
319
320
321 /**
322  * @brief set if the process should create a new session when started.
323  * @param enable determine if the process should start a new session.
324  * @return @a true iff the value of enable was accepted.
325  *
326  * If the process is already running, a new value is not accepted.
327  */
328 bool ProcessImplementation::setCreateNewSession( bool enable )
329 {
330    if (m_state != ProcessState::stopped and enable != m_create_new_session)
331    {
332       return false;
333    }
334    m_create_new_session= enable;
335    return true;
336 } // eo ProcessImplementation::setCreateNewSession(bool);
337
338
339 /**
340  * @brief sets a new nice increment.
341  * @param nice the desired nice increment.
342  * @return @a true if the value was accepted and - in case the process was already started -
343  * the nice value was successfully changed.
344  */
345 bool ProcessImplementation::setNice(int nice)
346 {
347    errno= 0;
348    if (m_state != ProcessState::stopped)
349    {
350       int delta= m_nice_inc + nice;
351       m_nice_inc= nice;
352       int res= ::nice(delta);
353       if (res == -1 and  errno !=0 )
354       {
355          return false;
356       }
357    }
358    else
359    {
360       m_nice_inc = nice;
361    }
362    return true;
363 } // eo ProcessImplementation::setNice(int)
364
365
366 /**
367  * @brief sets the work dir the process should be started with.
368  * @param workdir the workdir
369  * @return @a true if the new workdir was accepted.
370  *
371  * The method will return @a false if the process is already started.
372  * The workdir can only be set before the process is started.
373  */
374 bool ProcessImplementation::setWorkDir(const std::string& workdir)
375 {
376    if ( m_state != ProcessState::stopped and workdir != m_workdir)
377    {
378       return false;
379    }
380    if (not workdir.empty())
381    {
382        I2n::Stat stat(workdir);
383        if (not stat or not stat.is_directory())
384        {
385            return false;
386        }
387    }
388    m_workdir= workdir;
389    return true;
390 } // eo ProcessImplementation::setWorkDir(const std::string&)
391
392
393 /**
394  * @brief sets new arguments for the process (the path to the binary is kept).
395  *
396  * @param args the new cli arguments for the subprocess (replacing the old ones).
397  */
398 void ProcessImplementation::resetArgs( const std::vector< std::string >& args )
399 {
400    if (m_args.size() > 1)
401    {
402       m_args.erase( ++m_args.begin(), m_args.end());
403    }
404    std::copy( args.begin(), args.end(), std::back_inserter(m_args) );
405 } // eo ProcessImplementation::resetArgs(const std::vectors< std::string >&)
406
407
408 /**
409  * starts the new process.
410  * provides pipes for sending data to/ receiving data from the new process.
411  * Basically forks and execs the new process.
412  *
413  * @param stderr if not NULL the given object will be connected to stderr of the new process.
414  *  The object can then be used for reading the data from the process' stderr; but cannot be written to.
415  * (The object will be closed if it was open).
416  * If the constant @a ProcessImplementation::StderrOnStdout is passed then stderr of the new process will
417  * be written to the same channel as stdout (i.e. can be read from the process class instance like the
418  * normal output).
419  * If NULL then the stderr channel from the parent process will also be used by the child.
420  * @return @a true iff the new subprocess started.
421  */
422 bool ProcessImplementation::startProcess( IOImplementation2 *stderr )
423 {
424    bool stderr2stdout= false;
425    m_errno = 0;
426    m_input_buffer.clear();
427    if (m_pid > 0 && m_state != ProcessState::stopped)
428    {
429       // process still/already running...
430       return false;
431    }
432    m_exit_code= 0;
433
434    if (stderr == _StderrOnStdout)
435    {
436       stderr2stdout= true;
437       stderr= NULL;
438    }
439
440    int to_process_pipe[2];
441    int from_process_pipe[2];
442    int from_process_stderr_pipe[2]= { -1, -1 };
443
444    if ( ::pipe(to_process_pipe) )
445    {
446       m_errno= errno;
447       return false;
448    }
449    FdCloser closeTo0( to_process_pipe[0] );
450    FdCloser closeTo1( to_process_pipe[1] );
451    if ( ::pipe (from_process_pipe) )
452    {
453       m_errno= errno;
454       return false;
455    }
456    FdCloser closeFrom0( from_process_pipe[0] );
457    FdCloser closeFrom1( from_process_pipe[1] );
458    if (stderr)
459    {
460       if (stderr->opened()) stderr->close();
461       if ( ::pipe (from_process_stderr_pipe) )
462       {
463          m_errno= errno;
464          return false;
465       }
466    }
467    FdCloser closeFromErr0( from_process_stderr_pipe[0] );
468    FdCloser closeFromErr1( from_process_stderr_pipe[1] );
469
470    m_pid = ::fork();
471
472    if ( m_pid == (pid_t)-1 )
473    {
474       m_errno= errno;
475       m_pid= 0;
476       // error; something went wrong
477       return false;
478    }
479    else if (m_pid > 0)
480    {
481       // we are in the parent part
482
483       // keep the fd's we need and (later) close the other ones:
484       closeTo1.release(); // don't close this fd!
485       setWriteFd(to_process_pipe[1]);
486       closeFrom0.release(); // don't close this fd!
487       setReadFd(from_process_pipe[0]);
488
489       if (stderr)
490       {
491          closeFromErr0.release(); // don't close this fd!
492          stderr->setReadFd(from_process_stderr_pipe[0]);
493       }
494
495       m_state= ProcessState::running;
496       process::addChildProcess(m_pid,this);
497       DOUT(" started child with pid " << m_pid);
498       return true;
499    }
500    else // pid > 0
501    {
502       // we are in the child part
503
504       // dup the fd's for stdin/-out/-err into place:
505       ::dup2(to_process_pipe[0],0);
506       ::dup2(from_process_pipe[1],1);
507       if (stderr)
508       {
509          ::dup2(from_process_stderr_pipe[1],2);
510          ::close(from_process_stderr_pipe[0]); ::close(from_process_stderr_pipe[1]);
511       }
512       else if (stderr2stdout)
513       {
514          ::dup2(from_process_pipe[1],2);
515       }
516       // close what we don't need:
517       ::close(to_process_pipe[0]); ::close(to_process_pipe[1]);
518       ::close(from_process_pipe[0]); ::close(from_process_pipe[1]);
519
520       // set workdir if requested:
521       if (not m_workdir.empty())
522       {
523          int r= ::chdir( m_workdir.c_str() );
524          if (r !=0 )
525          {
526             //TODO?
527             exit(255);
528          }
529       }
530
531       //
532       // collect args:
533       char **argv= new char*[m_args.size()+1];
534       int i=0;
535       for(std::vector<std::string>::iterator it= m_args.begin();
536          it != m_args.end();
537          ++it,++i)
538       {
539          argv[i]= strdup( it->c_str() );
540       }
541       argv[i]= NULL;
542       // update nice level:
543       if (m_nice_inc)
544       {
545          nice(m_nice_inc);
546       }
547       // create a new session id if requested:
548       if (m_create_new_session)
549       {
550          setsid();
551       }
552       // execute:
553       execv(m_path.c_str(), argv);
554       // exit if exec failed
555       exit(255);
556       //cleanup! ... just joking; we exec or we exit, in either case the system cleans
557       // everything which needs to be cleaned up.
558    }
559    return false; // keep the compiler happy...
560 } // eo ProcessImplementation::startProcess()
561
562
563 /**
564  * convenience method for starting the child process.
565  * This method uses predefined enum values for the stderr handling mode.
566  *
567  * @param stderr_mode the desired stderr mode.
568  * @return @a true iff the child process was created.
569  */
570 bool ProcessImplementation::startProcess( ProcessImplementation::StderrMode stderr_mode )
571 {
572    switch (stderr_mode)
573    {
574       case UseParentsStderr:
575          return startProcess( _UseParentsStderr );
576
577       case StderrOnStdout:
578          return startProcess( _StderrOnStdout );
579    }
580    return false;
581 }; // eo ProcessImplementation::startProcess(ProcessImplementation::StderrMode)
582
583
584 /**
585  * stops the process.
586  *
587  * @todo think about a more intelligent handling...
588  */
589 void ProcessImplementation::stopProcess(bool force)
590 {
591    // TODO: do it somewhat more intelligent?!
592    if (force)
593    {
594       kill(Signal::KILL);
595       //TODO: set running state?
596    }
597    else
598    {
599       kill(Signal::TERM);
600    }
601 } // eo ProcessImplementation::stop(bool)
602
603
604
605 /**
606  * sends a signal to the child process.
607  * @param signal the Signal which should be send.
608  * @return @a true if the signal was sent; @a false if an error occured.
609  */
610 bool ProcessImplementation::kill(Signal signal)
611 {
612    m_errno = 0;
613    if (m_pid == 0 || m_pid == (pid_t)-1)
614    {
615       m_errno= ESRCH;
616       return false;
617    }
618    int res = ::kill(m_pid, signal);
619    if (res < 0)
620    {
621       m_errno= errno;
622       return false;
623    }
624    if (signal == Signal::CONT && m_state == ProcessState::suspended)
625    {
626       m_state = ProcessState::running;
627    }
628    return true;
629 } // eo ProcessImplementation::kill(Signal)
630
631
632
633 /**
634  * set a new child state with information gobbled by the child signal handler.
635  *
636  * @note This method should only be called by the process manager!
637  *
638  * @param pid the pid of the child process.
639  * @param status the new status value (as delivered by waitpid())
640  */
641 void ProcessImplementation::setChildState(pid_t pid, int status)
642 {
643    DOUT("setChildState("<<pid<<","<<status<<")   pid="<<m_pid);
644    if (pid != m_pid)
645    {
646       // old child... ignore!
647       return;
648    }
649    if (WIFSTOPPED(status))
650    {
651       DOUT("stopped");
652       // stopped:
653       int stopsignal = WSTOPSIG(status);
654       // make stop signal available in exit_code:
655       m_exit_code= (stopsignal << 8);
656       m_state= ProcessState::suspended;
657       return;
658    }
659 #ifdef WIFCONTINUED
660    if (WIFCONTINUED(status))
661    {
662       DOUT("continued");
663       // continued after a stop:
664       m_state= ProcessState::running;
665       return;
666    }
667 #endif
668    if (WIFEXITED(status))
669    {
670       DOUT("normal exit");
671       //normal exit:
672       m_exit_code= (0xff & WEXITSTATUS(status));
673       m_pid= 0;
674       close(Direction::out);
675       m_state= ProcessState::stopped;
676       m_signal_terminated();
677       return;
678    }
679    if (WIFSIGNALED(status))
680    {
681       DOUT("signaled stop");
682       // exit by signal:
683       int termsignal = WTERMSIG(status);
684       // make term signal available in exit code (normal exit codes are only 8 bit)
685       m_exit_code = (termsignal << 8);
686       m_pid= 0;
687       close(Direction::out);
688       m_state= ProcessState::stopped;
689       m_signal_terminated();
690       return;
691    }
692    // this point should never be reached...!!
693 } // eo ProcessImplementation::setChildState(pid_t,int)
694
695
696 /*
697  * implementation of ProcessManager
698  */
699
700 /// the instance of the process manager (highlander; there can be only one!)
701 ProcessManager* ProcessManager::the_instance= NULL;
702
703
704 ProcessManager::ProcessManager()
705 {
706    setWhenTime(0);
707 } // eo ProcessManager::ProcessManager
708
709
710 /**
711  * delivers the process manager instance (generate if it doesn't exist)
712  * @return the process manager instance
713  */
714 ProcessManager* ProcessManager::getInstance()
715 {
716    if (! the_instance)
717    {
718       the_instance = new ProcessManager();
719       _activate_manager = &ProcessManager::activateMe;
720    }
721    return the_instance;
722 } // eo ProcessManager::getInstance
723
724
725 /**
726  * activate the timer so it's handled by the next backend cycle
727  */
728 void ProcessManager::activateMe()
729 {
730    setWhenTime(0);
731    activate();
732 } // eo ProcessManager::activateMe
733
734
735 /**
736  * real work is done here.
737  * Processes the information collected by the child signal handler.
738  */
739 void ProcessManager::execute()
740 {
741    PidStateList pid_state_list;
742    {
743       // block child signals (within this scope)
744       ScopedSignalBlocker blocker( Signal::CHLD );
745       // and now fetch the list of pending information
746       // (simply swap with our local empty list)
747       std::swap(pid_state_list, pending_pid_states);
748       // reserve the desired (minimum) capacity
749       pending_pid_states.reserve( config::pid_pool_capacity );
750    }
751    ODOUT("exec, " << pid_state_list.size() << " entries");
752
753    // interpret states:
754    for(PidStateList::iterator it = pid_state_list.begin();
755       it != pid_state_list.end();
756       ++it)
757    {
758       pid_t pid  = it->first;
759       int status = it->second;
760       ODOUT("  pid=" << pid << ", status=" << status);
761       ProcessImplementation *process_obj;
762       if (process::findChildProcess(pid,process_obj))
763       {
764          ODOUT("  local managed child,  process_obj="<< process_obj);
765          // pid found in list:
766          if (!WIFSTOPPED(status)
767 #ifdef WIFCONTINUED
768             && !WIFCONTINUED(status)
769 #endif
770             )
771          {
772             // take it from list if the child exited:
773             process::removeChildProcess(pid,process_obj);
774          }
775          if (process_obj)
776          {
777             // give the process object a chance to handle the state change:
778             process_obj->setChildState(pid, status);
779          }
780       }
781       else
782       {
783          ODOUT("foreign child");
784          // pid not found in list:
785          /* NOTE: in a non threaded environment this pid must be from a child process which is not
786             managed by this process classes; since this method is called after all setup of a child process
787             is done (; especially entering the new child pid into our internal lists).
788          */
789          m_foreign_pid_states.push_back(*it);
790       }
791    }
792
793    // handle the foreign childs:
794    {
795       /* idea:
796        * fetch a (pid,status) from the list, erase it (to avoid reentrance problems)
797        * and fire the signal. If someone forks childs outside this module then he can
798        * connect to the signal and receive all necessary status information gobbled by
799        * our child handler.
800        */
801       while (! m_foreign_pid_states.empty())
802       {
803          PidStateList::iterator it= m_foreign_pid_states.begin();
804          pid_t pid  = it->first;
805          int status = it->second;
806          m_foreign_pid_states.erase(it);
807          m_foreign_child_state_changed_signal(pid,status);
808       }
809    }
810
811 } // eo ProcessManager::execute
812
813
814 } // eo namespace AsyncIo