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 "support/forkedcontr.h"
18 #include "support/forkedcall.h"
31 # include <sys/wait.h>
33 # ifndef CXX_GLOBAL_CSTD
39 #include <boost/bind.hpp>
54 string const getChildErrorMessage()
56 DWORD const error_code = ::GetLastError();
59 bool const ok = ::FormatMessage(
60 FORMAT_MESSAGE_ALLOCATE_BUFFER |
61 FORMAT_MESSAGE_FROM_SYSTEM,
63 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
64 (LPTSTR) &t_message, 0, 0
67 std::ostringstream ss;
68 ss << "LyX: Error waiting for child: " << error_code;
71 ss << ": " << (LPTSTR)t_message;
72 ::LocalFree(t_message);
74 ss << ": Error unknown.";
76 return ss.str().c_str();
81 // Ensure, that only one controller exists inside process
82 ForkedcallsController & ForkedcallsController::get()
84 static ForkedcallsController singleton;
89 ForkedcallsController::ForkedcallsController()
93 // open question: should we stop childs here?
94 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
95 // I want to print or something.
96 ForkedcallsController::~ForkedcallsController()
100 void ForkedcallsController::addCall(ForkedProcess const & newcall)
102 forkedCalls.push_back(newcall.clone());
106 // Check the list of dead children and emit any associated signals.
107 void ForkedcallsController::handleCompletedProcesses()
109 ListType::iterator it = forkedCalls.begin();
110 ListType::iterator end = forkedCalls.end();
112 ForkedProcessPtr actCall = *it;
113 bool remove_it = false;
116 HANDLE const hProcess = HANDLE(actCall->pid());
118 DWORD const wait_status = ::WaitForSingleObject(hProcess, 0);
120 switch (wait_status) {
124 case WAIT_OBJECT_0: {
126 if (!GetExitCodeProcess(hProcess, &exit_code)) {
127 lyxerr << "GetExitCodeProcess failed waiting for child\n"
128 << getChildErrorMessage() << std::endl;
129 // Child died, so pretend it returned 1
130 actCall->setRetValue(1);
132 actCall->setRetValue(exit_code);
138 lyxerr << "WaitForSingleObject failed waiting for child\n"
139 << getChildErrorMessage() << std::endl;
140 actCall->setRetValue(1);
145 pid_t pid = actCall->pid();
147 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
149 if (waitrpid == -1) {
150 lyxerr << "LyX: Error waiting for child: "
151 << strerror(errno) << endl;
153 // Child died, so pretend it returned 1
154 actCall->setRetValue(1);
157 } else if (waitrpid == 0) {
158 // Still running. Move on to the next child.
160 } else if (WIFEXITED(stat_loc)) {
161 // Ok, the return value goes into retval.
162 actCall->setRetValue(WEXITSTATUS(stat_loc));
165 } else if (WIFSIGNALED(stat_loc)) {
166 // Child died, so pretend it returned 1
167 actCall->setRetValue(1);
170 } else if (WIFSTOPPED(stat_loc)) {
171 lyxerr << "LyX: Child (pid: " << pid
172 << ") stopped on signal "
173 << WSTOPSIG(stat_loc)
174 << ". Waiting for child to finish." << endl;
177 lyxerr << "LyX: Something rotten happened while "
178 "waiting for child " << pid << endl;
180 // Child died, so pretend it returned 1
181 actCall->setRetValue(1);
187 forkedCalls.erase(it);
188 actCall->emitSignal();
190 /* start all over: emiting the signal can result
191 * in changing the list (Ab)
193 it = forkedCalls.begin();
201 ForkedcallsController::iterator
202 ForkedcallsController::find_pid(pid_t pid)
204 return find_if(forkedCalls.begin(), forkedCalls.end(),
205 bind(equal_to<pid_t>(),
206 bind(&Forkedcall::pid, _1),
211 // Kill the process prematurely and remove it from the list
212 // within tolerance secs
213 void ForkedcallsController::kill(pid_t pid, int tolerance)
215 ListType::iterator it = find_pid(pid);
216 if (it == forkedCalls.end())
219 (*it)->kill(tolerance);
220 forkedCalls.erase(it);
223 } // namespace support