3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup Nielsen
7 * \author Angus Leeming
9 * Full author contact details are available in file CREDITS
11 * A class for the control of child processes launched using
12 * fork() and execvp().
17 #include "forkedcontr.h"
18 #include "forkedcall.h"
19 #include "lyxfunctional.h"
22 #include "frontends/Timeout.h"
24 #include <boost/bind.hpp>
35 #ifndef CXX_GLOBAL_CSTD
39 // Ensure, that only one controller exists inside process
40 ForkedcallsController & ForkedcallsController::get()
42 static ForkedcallsController singleton;
47 ForkedcallsController::ForkedcallsController()
49 timeout_ = new Timeout(100, Timeout::ONETIME);
52 .connect(boost::bind(&ForkedcallsController::timer, this));
56 // open question: should we stop childs here?
57 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
58 // I want to print or something.
59 ForkedcallsController::~ForkedcallsController()
61 for (ListType::iterator it = forkedCalls.begin();
62 it != forkedCalls.end(); ++it) {
70 void ForkedcallsController::addCall(ForkedProcess const & newcall)
72 if (!timeout_->running())
75 forkedCalls.push_back(newcall.clone());
81 // Check the list and, if there is a stopped child, emit the signal.
82 void ForkedcallsController::timer()
84 ListType::size_type start_size = forkedCalls.size();
86 for (ListType::iterator it = forkedCalls.begin();
87 it != forkedCalls.end(); ++it) {
88 ForkedProcess * actCall = *it;
90 pid_t pid = actCall->pid();
92 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
93 bool remove_it = false;
96 lyxerr << "LyX: Error waiting for child: "
97 << strerror(errno) << endl;
99 // Child died, so pretend it returned 1
100 actCall->setRetValue(1);
103 } else if (waitrpid == 0) {
104 // Still running. Move on to the next child.
107 } else if (WIFEXITED(stat_loc)) {
108 // Ok, the return value goes into retval.
109 actCall->setRetValue(WEXITSTATUS(stat_loc));
112 } else if (WIFSIGNALED(stat_loc)) {
113 // Child died, so pretend it returned 1
114 actCall->setRetValue(1);
117 } else if (WIFSTOPPED(stat_loc)) {
118 lyxerr << "LyX: Child (pid: " << pid
119 << ") stopped on signal "
120 << WSTOPSIG(stat_loc)
121 << ". Waiting for child to finish." << endl;
124 lyxerr << "LyX: Something rotten happened while "
125 "waiting for child " << pid << endl;
127 // Child died, so pretend it returned 1
128 actCall->setRetValue(1);
133 // Emit signal and remove the item from the list
134 actCall->emitSignal();
136 // erase returns the next iterator, so decrement it
137 // to continue the loop.
138 ListType::iterator prev = it;
140 forkedCalls.erase(it);
145 if (!forkedCalls.empty()) {
149 if (start_size != forkedCalls.size())
154 // Return a vector of the pids of all the controlled processes.
155 vector<pid_t> const ForkedcallsController::getPIDs() const
159 if (forkedCalls.empty())
162 pids.resize(forkedCalls.size());
164 vector<pid_t>::iterator vit = pids.begin();
165 for (ListType::const_iterator lit = forkedCalls.begin();
166 lit != forkedCalls.end(); ++lit, ++vit) {
167 *vit = (*lit)->pid();
170 std::sort(pids.begin(), pids.end());
175 // Get the command string of the process.
176 string const ForkedcallsController::getCommand(pid_t pid) const
178 ListType::const_iterator it =
179 find_if(forkedCalls.begin(), forkedCalls.end(),
180 lyx::compare_memfun(&Forkedcall::pid, pid));
182 if (it == forkedCalls.end())
185 return (*it)->command();
189 // Kill the process prematurely and remove it from the list
190 // within tolerance secs
191 void ForkedcallsController::kill(pid_t pid, int tolerance)
193 ListType::iterator it =
194 find_if(forkedCalls.begin(), forkedCalls.end(),
195 lyx::compare_memfun(&Forkedcall::pid, pid));
197 if (it == forkedCalls.end())
200 (*it)->kill(tolerance);
201 forkedCalls.erase(it);
203 if (forkedCalls.empty()) {