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>
39 #ifndef CXX_GLOBAL_CSTD
47 // Ensure, that only one controller exists inside process
48 ForkedcallsController & ForkedcallsController::get()
50 static ForkedcallsController singleton;
55 ForkedcallsController::ForkedcallsController()
57 timeout_ = new Timeout(100, Timeout::ONETIME);
60 .connect(bind(&ForkedcallsController::timer, this));
64 // open question: should we stop childs here?
65 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
66 // I want to print or something.
67 ForkedcallsController::~ForkedcallsController()
69 for (ListType::iterator it = forkedCalls.begin();
70 it != forkedCalls.end(); ++it) {
78 void ForkedcallsController::addCall(ForkedProcess const & newcall)
80 if (!timeout_->running())
83 forkedCalls.push_back(newcall.clone().release());
89 // Check the list and, if there is a stopped child, emit the signal.
90 void ForkedcallsController::timer()
92 ListType::size_type start_size = forkedCalls.size();
94 ListType::iterator it = forkedCalls.begin();
95 ListType::iterator end = forkedCalls.end();
97 ForkedProcess * actCall = *it;
99 pid_t pid = actCall->pid();
101 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
102 bool remove_it = false;
104 if (waitrpid == -1) {
105 lyxerr << "LyX: Error waiting for child: "
106 << strerror(errno) << endl;
108 // Child died, so pretend it returned 1
109 actCall->setRetValue(1);
112 } else if (waitrpid == 0) {
113 // Still running. Move on to the next child.
115 } else if (WIFEXITED(stat_loc)) {
116 // Ok, the return value goes into retval.
117 actCall->setRetValue(WEXITSTATUS(stat_loc));
120 } else if (WIFSIGNALED(stat_loc)) {
121 // Child died, so pretend it returned 1
122 actCall->setRetValue(1);
125 } else if (WIFSTOPPED(stat_loc)) {
126 lyxerr << "LyX: Child (pid: " << pid
127 << ") stopped on signal "
128 << WSTOPSIG(stat_loc)
129 << ". Waiting for child to finish." << endl;
132 lyxerr << "LyX: Something rotten happened while "
133 "waiting for child " << pid << endl;
135 // Child died, so pretend it returned 1
136 actCall->setRetValue(1);
141 forkedCalls.erase(it);
143 actCall->emitSignal();
146 /* start all over: emiting the signal can result
147 * in changing the list (Ab)
149 it = forkedCalls.begin();
155 if (!forkedCalls.empty() && !timeout_->running()) {
159 if (start_size != forkedCalls.size())
164 // Return a vector of the pids of all the controlled processes.
165 vector<pid_t> const ForkedcallsController::getPIDs() const
169 if (forkedCalls.empty())
172 pids.resize(forkedCalls.size());
174 vector<pid_t>::iterator vit = pids.begin();
175 for (ListType::const_iterator lit = forkedCalls.begin();
176 lit != forkedCalls.end(); ++lit, ++vit) {
177 *vit = (*lit)->pid();
180 std::sort(pids.begin(), pids.end());
185 // Get the command string of the process.
186 string const ForkedcallsController::getCommand(pid_t pid) const
188 ListType::const_iterator it =
189 find_if(forkedCalls.begin(), forkedCalls.end(),
190 lyx::compare_memfun(&Forkedcall::pid, pid));
192 if (it == forkedCalls.end())
195 return (*it)->command();
199 // Kill the process prematurely and remove it from the list
200 // within tolerance secs
201 void ForkedcallsController::kill(pid_t pid, int tolerance)
203 ListType::iterator it =
204 find_if(forkedCalls.begin(), forkedCalls.end(),
205 lyx::compare_memfun(&Forkedcall::pid, pid));
207 if (it == forkedCalls.end())
210 (*it)->kill(tolerance);
211 forkedCalls.erase(it);
213 if (forkedCalls.empty()) {
218 } // namespace support