]> git.lyx.org Git - lyx.git/blob - src/support/forkedcontr.C
hopefully fix tex2lyx linking.
[lyx.git] / src / support / forkedcontr.C
1 /**
2  * \file forkedcontr.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup Nielsen
7  * \author Angus Leeming
8  *
9  * Full author contact details are available in file CREDITS.
10  *
11  * A class for the control of child processes launched using
12  * fork() and execvp().
13  */
14
15 #include <config.h>
16
17 #include "support/forkedcontr.h"
18 #include "support/forkedcall.h"
19
20 #include "debug.h"
21
22 #ifdef _WIN32
23 # include <sstream>
24 # include <windows.h>
25
26 #else
27 # include <cerrno>
28 # include <csignal>
29 # include <cstdlib>
30 # ifdef HAVE_UNISTD_H
31 #  include <unistd.h>
32 # endif
33 # include <sys/wait.h>
34
35 # ifndef CXX_GLOBAL_CSTD
36   using std::signal;
37   using std::strerror;
38 # endif
39 #endif
40
41 #include <boost/bind.hpp>
42
43 using boost::bind;
44
45 using std::endl;
46 using std::equal_to;
47 using std::find_if;
48
49 using std::string;
50 using std::vector;
51
52 namespace lyx {
53 namespace support {
54
55 #if defined(_WIN32)
56 string const getChildErrorMessage()
57 {
58         DWORD const error_code = ::GetLastError();
59
60         HLOCAL t_message = 0;
61         bool const ok = ::FormatMessage(
62                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
63                 FORMAT_MESSAGE_FROM_SYSTEM,
64                 0, error_code,
65                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
66                 (LPTSTR) &t_message, 0, 0
67                 ) != 0;
68
69         std::ostringstream ss;
70         ss << "LyX: Error waiting for child: " << error_code;
71
72         if (ok) {
73                 ss << ": " << (LPTSTR)t_message;
74                 ::LocalFree(t_message);
75         } else
76                 ss << ": Error unknown.";
77
78         return ss.str().c_str();
79 }
80 #endif
81
82
83 // Ensure, that only one controller exists inside process
84 ForkedcallsController & ForkedcallsController::get()
85 {
86         static ForkedcallsController singleton;
87         return singleton;
88 }
89
90
91 ForkedcallsController::ForkedcallsController()
92 {}
93
94
95 // open question: should we stop childs here?
96 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
97 // I want to print or something.
98 ForkedcallsController::~ForkedcallsController()
99 {}
100
101
102 void ForkedcallsController::addCall(ForkedProcess const & newcall)
103 {
104         forkedCalls.push_back(newcall.clone());
105 }
106
107
108 // Check the list of dead children and emit any associated signals.
109 void ForkedcallsController::handleCompletedProcesses()
110 {
111         ListType::iterator it  = forkedCalls.begin();
112         ListType::iterator end = forkedCalls.end();
113         while (it != end) {
114                 ForkedProcessPtr actCall = *it;
115                 bool remove_it = false;
116
117 #if defined(_WIN32)
118                 HANDLE const hProcess = HANDLE(actCall->pid());
119
120                 DWORD const wait_status = ::WaitForSingleObject(hProcess, 0);
121
122                 switch (wait_status) {
123                 case WAIT_TIMEOUT:
124                         // Still running
125                         break;
126                 case WAIT_OBJECT_0: {
127                         DWORD exit_code = 0;
128                         if (!GetExitCodeProcess(hProcess, &exit_code)) {
129                                 lyxerr << "GetExitCodeProcess failed waiting for child\n"
130                                        << getChildErrorMessage() << std::endl;
131                                 // Child died, so pretend it returned 1
132                                 actCall->setRetValue(1);
133                         } else {
134                                 actCall->setRetValue(exit_code);
135                         }
136                         remove_it = true;
137                         break;
138                 }
139                 case WAIT_FAILED:
140                         lyxerr << "WaitForSingleObject failed waiting for child\n"
141                                << getChildErrorMessage() << std::endl;
142                         actCall->setRetValue(1);
143                         remove_it = true;
144                         break;
145                 }
146 #else
147                 pid_t pid = actCall->pid();
148                 int stat_loc;
149                 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
150
151                 if (waitrpid == -1) {
152                         lyxerr << "LyX: Error waiting for child: "
153                                << strerror(errno) << endl;
154
155                         // Child died, so pretend it returned 1
156                         actCall->setRetValue(1);
157                         remove_it = true;
158
159                 } else if (waitrpid == 0) {
160                         // Still running. Move on to the next child.
161
162                 } else if (WIFEXITED(stat_loc)) {
163                         // Ok, the return value goes into retval.
164                         actCall->setRetValue(WEXITSTATUS(stat_loc));
165                         remove_it = true;
166
167                 } else if (WIFSIGNALED(stat_loc)) {
168                         // Child died, so pretend it returned 1
169                         actCall->setRetValue(1);
170                         remove_it = true;
171
172                 } else if (WIFSTOPPED(stat_loc)) {
173                         lyxerr << "LyX: Child (pid: " << pid
174                                << ") stopped on signal "
175                                << WSTOPSIG(stat_loc)
176                                << ". Waiting for child to finish." << endl;
177
178                 } else {
179                         lyxerr << "LyX: Something rotten happened while "
180                                 "waiting for child " << pid << endl;
181
182                         // Child died, so pretend it returned 1
183                         actCall->setRetValue(1);
184                         remove_it = true;
185                 }
186 #endif
187
188                 if (remove_it) {
189                         forkedCalls.erase(it);
190                         actCall->emitSignal();
191
192                         /* start all over: emiting the signal can result
193                          * in changing the list (Ab)
194                          */
195                         it = forkedCalls.begin();
196                 } else {
197                         ++it;
198                 }
199         }
200 }
201
202
203 ForkedcallsController::iterator
204 ForkedcallsController::find_pid(pid_t pid)
205 {
206         return find_if(forkedCalls.begin(), forkedCalls.end(),
207                        bind(equal_to<pid_t>(),
208                             bind(&Forkedcall::pid, _1),
209                             pid));
210 }
211
212
213 // Kill the process prematurely and remove it from the list
214 // within tolerance secs
215 void ForkedcallsController::kill(pid_t pid, int tolerance)
216 {
217         ListType::iterator it = find_pid(pid);
218         if (it == forkedCalls.end())
219                 return;
220
221         (*it)->kill(tolerance);
222         forkedCalls.erase(it);
223 }
224
225 } // namespace support
226 } // namespace lyx