Commit | Line | Data |
---|---|---|
8c15b8c7 TJ |
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 | */ | |
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 | ||
56 | namespace | |
57 | { | |
58 | ||
42b7c46d | 59 | using namespace AsyncIo; |
5c8a3d40 RP |
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 | ||
42b7c46d | 214 | namespace 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 | */ | |
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 | { | |
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 | */ | |
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 | } | |
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 | */ | |
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 | ||
42b7c46d | 840 | } // eo namespace AsyncIo |