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