]> git.lyx.org Git - lyx.git/blob - src/support/forkedcontr.C
Enable compilation on various platforms.
[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 # include <unistd.h>
31 # include <sys/wait.h>
32
33 # ifndef CXX_GLOBAL_CSTD
34   using std::signal;
35   using std::strerror;
36 # endif
37 #endif
38
39 #include <boost/bind.hpp>
40
41 using boost::bind;
42
43 using std::endl;
44 using std::equal_to;
45 using std::find_if;
46
47 using std::string;
48 using std::vector;
49
50 namespace lyx {
51 namespace support {
52
53 #if defined(_WIN32)
54 string const getChildErrorMessage()
55 {
56         DWORD const error_code = ::GetLastError();
57
58         HLOCAL t_message = 0;
59         bool const ok = ::FormatMessage(
60                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
61                 FORMAT_MESSAGE_FROM_SYSTEM,
62                 0, error_code,
63                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
64                 (LPTSTR) &t_message, 0, 0
65                 ) != 0;
66
67         std::ostringstream ss;
68         ss << "LyX: Error waiting for child: " << error_code;
69
70         if (ok) {
71                 ss << ": " << (LPTSTR)t_message;
72                 ::LocalFree(t_message);
73         } else
74                 ss << ": Error unknown.";
75
76         return ss.str().c_str();
77 }
78 #endif
79
80
81 // Ensure, that only one controller exists inside process
82 ForkedcallsController & ForkedcallsController::get()
83 {
84         static ForkedcallsController singleton;
85         return singleton;
86 }
87
88
89 ForkedcallsController::ForkedcallsController()
90 {}
91
92
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()
97 {}
98
99
100 void ForkedcallsController::addCall(ForkedProcess const & newcall)
101 {
102         forkedCalls.push_back(newcall.clone());
103 }
104
105
106 // Check the list of dead children and emit any associated signals.
107 void ForkedcallsController::handleCompletedProcesses()
108 {
109         ListType::iterator it  = forkedCalls.begin();
110         ListType::iterator end = forkedCalls.end();
111         while (it != end) {
112                 ForkedProcessPtr actCall = *it;
113                 bool remove_it = false;
114
115 #if defined(_WIN32)
116                 HANDLE const hProcess = HANDLE(actCall->pid());
117
118                 DWORD const wait_status = ::WaitForSingleObject(hProcess, 0);
119
120                 switch (wait_status) {
121                 case WAIT_TIMEOUT:
122                         // Still running
123                         break;
124                 case WAIT_OBJECT_0: {
125                         DWORD exit_code = 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);
131                         } else {
132                                 actCall->setRetValue(exit_code);
133                         }
134                         remove_it = true;
135                         break;
136                 }
137                 case WAIT_FAILED:
138                         lyxerr << "WaitForSingleObject failed waiting for child\n"
139                                << getChildErrorMessage() << std::endl;
140                         actCall->setRetValue(1);
141                         remove_it = true;
142                         break;
143                 }
144 #else
145                 pid_t pid = actCall->pid();
146                 int stat_loc;
147                 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
148
149                 if (waitrpid == -1) {
150                         lyxerr << "LyX: Error waiting for child: "
151                                << strerror(errno) << endl;
152
153                         // Child died, so pretend it returned 1
154                         actCall->setRetValue(1);
155                         remove_it = true;
156
157                 } else if (waitrpid == 0) {
158                         // Still running. Move on to the next child.
159
160                 } else if (WIFEXITED(stat_loc)) {
161                         // Ok, the return value goes into retval.
162                         actCall->setRetValue(WEXITSTATUS(stat_loc));
163                         remove_it = true;
164
165                 } else if (WIFSIGNALED(stat_loc)) {
166                         // Child died, so pretend it returned 1
167                         actCall->setRetValue(1);
168                         remove_it = true;
169
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;
175
176                 } else {
177                         lyxerr << "LyX: Something rotten happened while "
178                                 "waiting for child " << pid << endl;
179
180                         // Child died, so pretend it returned 1
181                         actCall->setRetValue(1);
182                         remove_it = true;
183                 }
184 #endif
185
186                 if (remove_it) {
187                         forkedCalls.erase(it);
188                         actCall->emitSignal();
189
190                         /* start all over: emiting the signal can result
191                          * in changing the list (Ab)
192                          */
193                         it = forkedCalls.begin();
194                 } else {
195                         ++it;
196                 }
197         }
198 }
199
200
201 ForkedcallsController::iterator
202 ForkedcallsController::find_pid(pid_t pid)
203 {
204         return find_if(forkedCalls.begin(), forkedCalls.end(),
205                        bind(equal_to<pid_t>(),
206                             bind(&Forkedcall::pid, _1),
207                             pid));
208 }
209
210
211 // Kill the process prematurely and remove it from the list
212 // within tolerance secs
213 void ForkedcallsController::kill(pid_t pid, int tolerance)
214 {
215         ListType::iterator it = find_pid(pid);
216         if (it == forkedCalls.end())
217                 return;
218
219         (*it)->kill(tolerance);
220         forkedCalls.erase(it);
221 }
222
223 } // namespace support
224 } // namespace lyx