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