2 * \file ForkedCalls.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Angus Leeming
8 * \author Alfredo Braunstein
10 * Full author contact details are available in file CREDITS.
15 #include "support/ForkedCalls.h"
17 #include "support/debug.h"
18 #include "support/filetools.h"
19 #include "support/lstrings.h"
20 #include "support/lyxlib.h"
21 #include "support/os.h"
22 #include "support/Timeout.h"
24 #include <boost/bind.hpp>
41 # include <sys/wait.h>
42 # ifndef CXX_GLOBAL_CSTD
62 /////////////////////////////////////////////////////////////////////
66 /////////////////////////////////////////////////////////////////////
68 class Murder : public boost::signals::trackable {
71 static void killItDead(int secs, pid_t pid)
74 new Murder(secs, pid);
75 } else if (pid != 0) {
76 support::kill(pid, SIGKILL);
84 support::kill(pid_, SIGKILL);
85 lyxerr << "Killed " << pid_ << std::endl;
91 Murder(int secs, pid_t pid)
92 : timeout_(0), pid_(pid)
94 timeout_ = new Timeout(1000*secs, Timeout::ONETIME);
95 timeout_->timeout.connect(boost::bind(&Murder::kill, this));
113 /////////////////////////////////////////////////////////////////////
117 /////////////////////////////////////////////////////////////////////
119 ForkedProcess::ForkedProcess()
120 : pid_(0), retval_(0)
124 void ForkedProcess::emitSignal()
127 signal_->operator()(pid_, retval_);
132 // Spawn the child process
133 int ForkedProcess::run(Starttype type)
136 pid_ = generateChild();
137 if (pid_ <= 0) { // child or fork failed.
144 retval_ = waitForChild();
147 // Integrate into the Controller
148 ForkedCallsController & contr = ForkedCallsController::get();
149 contr.addCall(*this);
158 bool ForkedProcess::running() const
163 #if !defined (_WIN32)
164 // Un-UNIX like, but we don't have much use for
165 // knowing if a zombie exists, so just reap it first.
167 waitpid(pid(), &waitstatus, WNOHANG);
170 // Racy of course, but it will do.
171 if (support::kill(pid(), 0) && errno == ESRCH)
177 void ForkedProcess::kill(int tol)
179 lyxerr << "ForkedProcess::kill(" << tol << ')' << endl;
181 lyxerr << "Can't kill non-existent process!" << endl;
185 // The weird (std::max)(a,b) signature prevents expansion
186 // of an evil MSVC macro.
187 int const tolerance = (std::max)(0, tol);
188 if (tolerance == 0) {
190 Murder::killItDead(0, pid());
193 int ret = support::kill(pid(), SIGHUP);
195 // The process is already dead if wait_for_death is false
196 bool const wait_for_death = (ret == 0 && errno != ESRCH);
198 if (wait_for_death) {
199 Murder::killItDead(tolerance, pid());
205 // Wait for child process to finish. Returns returncode from child.
206 int ForkedProcess::waitForChild()
208 // We'll pretend that the child returns 1 on all error conditions.
212 HANDLE const hProcess = HANDLE(pid_);
214 DWORD const wait_status = ::WaitForSingleObject(hProcess, INFINITE);
216 switch (wait_status) {
217 case WAIT_OBJECT_0: {
219 if (!GetExitCodeProcess(hProcess, &exit_code)) {
220 lyxerr << "GetExitCodeProcess failed waiting for child\n"
221 << getChildErrorMessage() << std::endl;
227 lyxerr << "WaitForSingleObject failed waiting for child\n"
228 << getChildErrorMessage() << std::endl;
236 pid_t waitrpid = waitpid(pid_, &status, WUNTRACED);
237 if (waitrpid == -1) {
238 lyxerr << "LyX: Error waiting for child:"
239 << strerror(errno) << endl;
241 } else if (WIFEXITED(status)) {
242 // Child exited normally. Update return value.
243 retval_ = WEXITSTATUS(status);
245 } else if (WIFSIGNALED(status)) {
246 lyxerr << "LyX: Child didn't catch signal "
248 << "and died. Too bad." << endl;
250 } else if (WIFSTOPPED(status)) {
251 lyxerr << "LyX: Child (pid: " << pid_
252 << ") stopped on signal "
254 << ". Waiting for child to finish." << endl;
256 lyxerr << "LyX: Something rotten happened while "
257 "waiting for child " << pid_ << endl;
266 /////////////////////////////////////////////////////////////////////
270 /////////////////////////////////////////////////////////////////////
273 int ForkedCall::startScript(Starttype wait, string const & what)
276 retval_ = startScript(what, SignalTypePtr());
286 int ForkedCall::startScript(string const & what, SignalTypePtr signal)
291 return run(DontWait);
295 // generate child in background
296 int ForkedCall::generateChild()
298 string line = trim(command_);
302 // Split the input command up into an array of words stored
303 // in a contiguous block of memory. The array contains pointers
305 // Don't forget the terminating `\0' character.
306 char const * const c_str = line.c_str();
307 vector<char> vec(c_str, c_str + line.size() + 1);
309 // Splitting the command up into an array of words means replacing
310 // the whitespace between words with '\0'. Life is complicated
311 // however, because words protected by quotes can contain whitespace.
313 // The strategy we adopt is:
314 // 1. If we're not inside quotes, then replace white space with '\0'.
315 // 2. If we are inside quotes, then don't replace the white space
316 // but do remove the quotes themselves. We do this naively by
317 // replacing the quote with '\0' which is fine if quotes
318 // delimit the entire word.
319 char inside_quote = 0;
320 vector<char>::iterator it = vec.begin();
321 vector<char>::iterator const end = vec.end();
322 for (; it != end; ++it) {
327 else if (c == '\'' || c == '"') {
330 // spawnvp *requires* the quotes or it will
331 // split the arg at the internal whitespace!
332 // Make shure the quote is a DOS-style one.
339 } else if (c == inside_quote) {
349 // Build an array of pointers to each word.
353 for (; it != end; ++it) {
354 if (*it != '\0' && prev == '\0')
355 argv.push_back(&*it);
361 if (lyxerr.debugging(Debug::FILES)) {
362 vector<char *>::iterator ait = argv.begin();
363 vector<char *>::iterator const aend = argv.end();
364 lyxerr << "<command>\n\t" << line
365 << "\n\tInterpretted as:\n\n";
366 for (; ait != aend; ++ait)
368 lyxerr << '\t'<< *ait << '\n';
369 lyxerr << "</command>" << std::endl;
373 pid_t const cpid = spawnvp(_P_NOWAIT, argv[0], &*argv.begin());
375 pid_t const cpid = ::fork();
378 execvp(argv[0], &*argv.begin());
380 // If something goes wrong, we end up here
381 lyxerr << "execvp of \"" << command_ << "\" failed: "
382 << strerror(errno) << endl;
389 lyxerr << "Could not fork: " << strerror(errno) << endl;
396 /////////////////////////////////////////////////////////////////////
400 /////////////////////////////////////////////////////////////////////
403 ForkedCallQueue & ForkedCallQueue::get()
405 static ForkedCallQueue singleton;
410 ForkedCall::SignalTypePtr ForkedCallQueue::add(string const & process)
412 ForkedCall::SignalTypePtr ptr;
413 ptr.reset(new ForkedCall::SignalType);
414 callQueue_.push(Process(process, ptr));
421 void ForkedCallQueue::callNext()
423 if (callQueue_.empty())
425 Process pro = callQueue_.front();
427 // Bind our chain caller
428 pro.second->connect(boost::bind(&ForkedCallQueue::callback,
431 // If we fail to fork the process, then emit the signal
432 // to tell the outside world that it failed.
433 if (call.startScript(pro.first, pro.second) > 0)
434 pro.second->operator()(0,1);
438 void ForkedCallQueue::callback(pid_t, int)
440 if (callQueue_.empty())
447 ForkedCallQueue::ForkedCallQueue()
452 void ForkedCallQueue::startCaller()
454 LYXERR(Debug::GRAPHICS, "ForkedCallQueue: waking up");
460 void ForkedCallQueue::stopCaller()
463 LYXERR(Debug::GRAPHICS, "ForkedCallQueue: I'm going to sleep");
467 bool ForkedCallQueue::running() const
473 /////////////////////////////////////////////////////////////////////
475 // ForkedCallsController
477 /////////////////////////////////////////////////////////////////////
480 string const getChildErrorMessage()
482 DWORD const error_code = ::GetLastError();
484 HLOCAL t_message = 0;
485 bool const ok = ::FormatMessage(
486 FORMAT_MESSAGE_ALLOCATE_BUFFER |
487 FORMAT_MESSAGE_FROM_SYSTEM,
489 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
490 (LPTSTR) &t_message, 0, 0
493 std::ostringstream ss;
494 ss << "LyX: Error waiting for child: " << error_code;
497 ss << ": " << (LPTSTR)t_message;
498 ::LocalFree(t_message);
500 ss << ": Error unknown.";
507 // Ensure, that only one controller exists inside process
508 ForkedCallsController & ForkedCallsController::get()
510 static ForkedCallsController singleton;
515 ForkedCallsController::ForkedCallsController()
519 // open question: should we stop childs here?
520 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
521 // I want to print or something.
522 ForkedCallsController::~ForkedCallsController()
526 void ForkedCallsController::addCall(ForkedProcess const & newcall)
528 forkedCalls.push_back(newcall.clone());
532 // Check the list of dead children and emit any associated signals.
533 void ForkedCallsController::handleCompletedProcesses()
535 ListType::iterator it = forkedCalls.begin();
536 ListType::iterator end = forkedCalls.end();
538 ForkedProcessPtr actCall = *it;
539 bool remove_it = false;
542 HANDLE const hProcess = HANDLE(actCall->pid());
544 DWORD const wait_status = ::WaitForSingleObject(hProcess, 0);
546 switch (wait_status) {
550 case WAIT_OBJECT_0: {
552 if (!GetExitCodeProcess(hProcess, &exit_code)) {
553 lyxerr << "GetExitCodeProcess failed waiting for child\n"
554 << getChildErrorMessage() << std::endl;
555 // Child died, so pretend it returned 1
556 actCall->setRetValue(1);
558 actCall->setRetValue(exit_code);
564 lyxerr << "WaitForSingleObject failed waiting for child\n"
565 << getChildErrorMessage() << std::endl;
566 actCall->setRetValue(1);
571 pid_t pid = actCall->pid();
573 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
575 if (waitrpid == -1) {
576 lyxerr << "LyX: Error waiting for child: "
577 << strerror(errno) << endl;
579 // Child died, so pretend it returned 1
580 actCall->setRetValue(1);
583 } else if (waitrpid == 0) {
584 // Still running. Move on to the next child.
586 } else if (WIFEXITED(stat_loc)) {
587 // Ok, the return value goes into retval.
588 actCall->setRetValue(WEXITSTATUS(stat_loc));
591 } else if (WIFSIGNALED(stat_loc)) {
592 // Child died, so pretend it returned 1
593 actCall->setRetValue(1);
596 } else if (WIFSTOPPED(stat_loc)) {
597 lyxerr << "LyX: Child (pid: " << pid
598 << ") stopped on signal "
599 << WSTOPSIG(stat_loc)
600 << ". Waiting for child to finish." << endl;
603 lyxerr << "LyX: Something rotten happened while "
604 "waiting for child " << pid << endl;
606 // Child died, so pretend it returned 1
607 actCall->setRetValue(1);
613 forkedCalls.erase(it);
614 actCall->emitSignal();
616 /* start all over: emiting the signal can result
617 * in changing the list (Ab)
619 it = forkedCalls.begin();
627 ForkedCallsController::iterator ForkedCallsController::find_pid(pid_t pid)
629 return find_if(forkedCalls.begin(), forkedCalls.end(),
630 bind(equal_to<pid_t>(),
631 bind(&ForkedCall::pid, _1),
636 // Kill the process prematurely and remove it from the list
637 // within tolerance secs
638 void ForkedCallsController::kill(pid_t pid, int tolerance)
640 ListType::iterator it = find_pid(pid);
641 if (it == forkedCalls.end())
644 (*it)->kill(tolerance);
645 forkedCalls.erase(it);
648 } // namespace support