in startProcess use _exit instead of exit after fork in child; test command with...
[libasyncio] / asyncio / async_process.cpp
CommitLineData
8c15b8c7
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
5c8a3d40
RP
20/** @file
21 *
5c8a3d40 22 * (c) Copyright 2007-2008 by Intra2net AG
5c8a3d40
RP
23 */
24
25//#define NOISEDEBUG
26
42b7c46d 27#include "async_process.hpp"
5c8a3d40
RP
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
ce4ab835 40//#include <filefunc.hxx>
5c8a3d40
RP
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
56namespace
57{
58
42b7c46d 59using namespace AsyncIo;
5c8a3d40
RP
60
61/**
62 * local configuration values
63 */
64namespace 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)
75void (*oldChildHandler)(int) = NULL;
76
77/// method pointer for activating process manager
78void (ProcessManager::*_activate_manager)();
79
80PidStateList 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 */
87void 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
105namespace process
106{
107
108typedef std::pair<pid_t, ProcessImplementation*> PidProcPair;
109typedef std::list< PidProcPair > PidProcList;
110
111
112template< typename F, typename S >
113struct 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
121std::list<ProcessImplementation*> g_process_list;
122PidProcList g_pid_list;
123
124
125void addProcessInstance( ProcessImplementation* obj )
126{
127 g_process_list.push_back(obj);
128} // eo addProcessInstance(ProcessImplementation*)
129
130
131void 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
148void addChildProcess( pid_t pid, ProcessImplementation* obj)
149{
150 g_pid_list.push_back ( PidProcPair(pid,obj) );
151} // eo addChildProcess(pid_t,ProcessImplementation*)
152
153
154void 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
166bool 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 */
194struct 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
42b7c46d 214namespace AsyncIo
5c8a3d40
RP
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 */
227bool 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 */
254bool 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
277IOImplementation2* ProcessImplementation::_StderrOnStdout = ((IOImplementation2*) 1);
278IOImplementation2* 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 */
289ProcessImplementation::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
307ProcessImplementation::~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
317void 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 */
331PushBackFiller<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 */
344bool 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 */
361bool 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 */
390bool 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 {
ce4ab835 398 Utils::FileStat stat(workdir);
5c8a3d40
RP
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 */
414void 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 */
438bool 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 }
c3b830a2
CH
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
5c8a3d40
RP
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?
c3b830a2 551 _exit(255);
5c8a3d40
RP
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
c3b830a2 579 _exit(255);
5c8a3d40
RP
580 //cleanup! ... just joking; we exec or we exit, in either case the system cleans
581 // everything which needs to be cleaned up.
c3b830a2
CH
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
5c8a3d40
RP
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 */
596bool 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 */
615void 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 */
636bool 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 */
667void 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!)
727ProcessManager* ProcessManager::the_instance= NULL;
728
729
730ProcessManager::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 */
740ProcessManager* 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 */
754void 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 */
765void 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
42b7c46d 840} // eo namespace AsyncIo