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>
44 # include <sys/wait.h>
56 /////////////////////////////////////////////////////////////////////
60 /////////////////////////////////////////////////////////////////////
62 class Murder : public boost::signals::trackable {
65 static void killItDead(int secs, pid_t pid)
68 new Murder(secs, pid);
70 support::kill(pid, SIGKILL);
77 support::kill(pid_, SIGKILL);
78 lyxerr << "Killed " << pid_ << endl;
84 Murder(int secs, pid_t pid)
85 : timeout_(1000*secs, Timeout::ONETIME), pid_(pid)
87 timeout_.timeout.connect(boost::bind(&Murder::kill, this));
100 /////////////////////////////////////////////////////////////////////
104 /////////////////////////////////////////////////////////////////////
106 ForkedProcess::ForkedProcess()
107 : pid_(0), retval_(0)
111 void ForkedProcess::emitSignal()
114 signal_->operator()(pid_, retval_);
119 // Spawn the child process
120 int ForkedProcess::run(Starttype type)
123 pid_ = generateChild();
124 if (pid_ <= 0) { // child or fork failed.
131 retval_ = waitForChild();
134 // Integrate into the Controller
135 ForkedCallsController::addCall(*this);
144 bool ForkedProcess::running() const
149 #if !defined (_WIN32)
150 // Un-UNIX like, but we don't have much use for
151 // knowing if a zombie exists, so just reap it first.
153 waitpid(pid(), &waitstatus, WNOHANG);
156 // Racy of course, but it will do.
157 if (support::kill(pid(), 0) && errno == ESRCH)
163 void ForkedProcess::kill(int tol)
165 lyxerr << "ForkedProcess::kill(" << tol << ')' << endl;
167 lyxerr << "Can't kill non-existent process!" << endl;
171 int const tolerance = max(0, tol);
172 if (tolerance == 0) {
174 Murder::killItDead(0, pid());
176 int ret = support::kill(pid(), SIGHUP);
178 // The process is already dead if wait_for_death is false
179 bool const wait_for_death = (ret == 0 && errno != ESRCH);
182 Murder::killItDead(tolerance, pid());
187 // Wait for child process to finish. Returns returncode from child.
188 int ForkedProcess::waitForChild()
190 // We'll pretend that the child returns 1 on all error conditions.
194 HANDLE const hProcess = HANDLE(pid_);
196 DWORD const wait_status = ::WaitForSingleObject(hProcess, INFINITE);
198 switch (wait_status) {
199 case WAIT_OBJECT_0: {
201 if (!GetExitCodeProcess(hProcess, &exit_code)) {
202 lyxerr << "GetExitCodeProcess failed waiting for child\n"
203 << getChildErrorMessage() << endl;
209 lyxerr << "WaitForSingleObject failed waiting for child\n"
210 << getChildErrorMessage() << endl;
218 pid_t waitrpid = waitpid(pid_, &status, WUNTRACED);
219 if (waitrpid == -1) {
220 lyxerr << "LyX: Error waiting for child:"
221 << strerror(errno) << endl;
223 } else if (WIFEXITED(status)) {
224 // Child exited normally. Update return value.
225 retval_ = WEXITSTATUS(status);
227 } else if (WIFSIGNALED(status)) {
228 lyxerr << "LyX: Child didn't catch signal "
230 << "and died. Too bad." << endl;
232 } else if (WIFSTOPPED(status)) {
233 lyxerr << "LyX: Child (pid: " << pid_
234 << ") stopped on signal "
236 << ". Waiting for child to finish." << endl;
238 lyxerr << "LyX: Something rotten happened while "
239 "waiting for child " << pid_ << endl;
248 /////////////////////////////////////////////////////////////////////
252 /////////////////////////////////////////////////////////////////////
255 int ForkedCall::startScript(Starttype wait, string const & what)
258 retval_ = startScript(what, SignalTypePtr());
268 int ForkedCall::startScript(string const & what, SignalTypePtr signal)
273 return run(DontWait);
277 // generate child in background
278 int ForkedCall::generateChild()
280 string line = trim(command_);
284 // Split the input command up into an array of words stored
285 // in a contiguous block of memory. The array contains pointers
287 // Don't forget the terminating `\0' character.
288 char const * const c_str = line.c_str();
289 vector<char> vec(c_str, c_str + line.size() + 1);
291 // Splitting the command up into an array of words means replacing
292 // the whitespace between words with '\0'. Life is complicated
293 // however, because words protected by quotes can contain whitespace.
295 // The strategy we adopt is:
296 // 1. If we're not inside quotes, then replace white space with '\0'.
297 // 2. If we are inside quotes, then don't replace the white space
298 // but do remove the quotes themselves. We do this naively by
299 // replacing the quote with '\0' which is fine if quotes
300 // delimit the entire word.
301 char inside_quote = 0;
302 vector<char>::iterator it = vec.begin();
303 vector<char>::iterator const end = vec.end();
304 for (; it != end; ++it) {
309 else if (c == '\'' || c == '"') {
312 // spawnvp *requires* the quotes or it will
313 // split the arg at the internal whitespace!
314 // Make shure the quote is a DOS-style one.
321 } else if (c == inside_quote) {
331 // Build an array of pointers to each word.
335 for (; it != end; ++it) {
336 if (*it != '\0' && prev == '\0')
337 argv.push_back(&*it);
343 if (lyxerr.debugging(Debug::FILES)) {
344 vector<char *>::iterator ait = argv.begin();
345 vector<char *>::iterator const aend = argv.end();
346 lyxerr << "<command>\n\t" << line
347 << "\n\tInterpretted as:\n\n";
348 for (; ait != aend; ++ait)
350 lyxerr << '\t'<< *ait << '\n';
351 lyxerr << "</command>" << endl;
355 pid_t const cpid = spawnvp(_P_NOWAIT, argv[0], &*argv.begin());
357 pid_t const cpid = ::fork();
360 execvp(argv[0], &*argv.begin());
362 // If something goes wrong, we end up here
363 lyxerr << "execvp of \"" << command_ << "\" failed: "
364 << strerror(errno) << endl;
371 lyxerr << "Could not fork: " << strerror(errno) << endl;
378 /////////////////////////////////////////////////////////////////////
382 /////////////////////////////////////////////////////////////////////
384 namespace ForkedCallQueue {
386 /// A process in the queue
387 typedef pair<string, ForkedCall::SignalTypePtr> Process;
388 /** Add a process to the queue. Processes are forked sequentially
389 * only one is running at a time.
390 * Connect to the returned signal and you'll be informed when
391 * the process has ended.
393 ForkedCall::SignalTypePtr add(string const & process);
395 /// in-progress queue
396 static queue<Process> callQueue_;
398 /// flag whether queue is running
399 static bool running_ = 0;
406 void callback(pid_t, int);
408 ForkedCall::SignalTypePtr add(string const & process)
410 ForkedCall::SignalTypePtr ptr;
411 ptr.reset(new ForkedCall::SignalType);
412 callQueue_.push(Process(process, ptr));
421 if (callQueue_.empty())
423 Process pro = callQueue_.front();
425 // Bind our chain caller
426 pro.second->connect(boost::bind(&ForkedCallQueue::callback, _1, _2));
428 // If we fail to fork the process, then emit the signal
429 // to tell the outside world that it failed.
430 if (call.startScript(pro.first, pro.second) > 0)
431 pro.second->operator()(0,1);
435 void callback(pid_t, int)
437 if (callQueue_.empty())
446 LYXERR(Debug::GRAPHICS, "ForkedCallQueue: waking up");
455 LYXERR(Debug::GRAPHICS, "ForkedCallQueue: I'm going to sleep");
464 } // namespace ForkedCallsQueue
468 /////////////////////////////////////////////////////////////////////
470 // ForkedCallsController
472 /////////////////////////////////////////////////////////////////////
475 string const getChildErrorMessage()
477 DWORD const error_code = ::GetLastError();
479 HLOCAL t_message = 0;
480 bool const ok = ::FormatMessage(
481 FORMAT_MESSAGE_ALLOCATE_BUFFER |
482 FORMAT_MESSAGE_FROM_SYSTEM,
484 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
485 (LPTSTR) &t_message, 0, 0
489 ss << "LyX: Error waiting for child: " << error_code;
492 ss << ": " << (LPTSTR)t_message;
493 ::LocalFree(t_message);
495 ss << ": Error unknown.";
502 namespace ForkedCallsController {
504 typedef boost::shared_ptr<ForkedProcess> ForkedProcessPtr;
505 typedef list<ForkedProcessPtr> ListType;
506 typedef ListType::iterator iterator;
509 /// The child processes
510 static ListType forkedCalls;
512 iterator find_pid(pid_t pid)
514 return find_if(forkedCalls.begin(), forkedCalls.end(),
515 bind(equal_to<pid_t>(),
516 bind(&ForkedCall::pid, _1),
521 void addCall(ForkedProcess const & newcall)
523 forkedCalls.push_back(newcall.clone());
527 // Check the list of dead children and emit any associated signals.
528 void handleCompletedProcesses()
530 ListType::iterator it = forkedCalls.begin();
531 ListType::iterator end = forkedCalls.end();
533 ForkedProcessPtr actCall = *it;
534 bool remove_it = false;
537 HANDLE const hProcess = HANDLE(actCall->pid());
539 DWORD const wait_status = ::WaitForSingleObject(hProcess, 0);
541 switch (wait_status) {
545 case WAIT_OBJECT_0: {
547 if (!GetExitCodeProcess(hProcess, &exit_code)) {
548 lyxerr << "GetExitCodeProcess failed waiting for child\n"
549 << getChildErrorMessage() << endl;
550 // Child died, so pretend it returned 1
551 actCall->setRetValue(1);
553 actCall->setRetValue(exit_code);
559 lyxerr << "WaitForSingleObject failed waiting for child\n"
560 << getChildErrorMessage() << endl;
561 actCall->setRetValue(1);
566 pid_t pid = actCall->pid();
568 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
570 if (waitrpid == -1) {
571 lyxerr << "LyX: Error waiting for child: "
572 << strerror(errno) << endl;
574 // Child died, so pretend it returned 1
575 actCall->setRetValue(1);
578 } else if (waitrpid == 0) {
579 // Still running. Move on to the next child.
581 } else if (WIFEXITED(stat_loc)) {
582 // Ok, the return value goes into retval.
583 actCall->setRetValue(WEXITSTATUS(stat_loc));
586 } else if (WIFSIGNALED(stat_loc)) {
587 // Child died, so pretend it returned 1
588 actCall->setRetValue(1);
591 } else if (WIFSTOPPED(stat_loc)) {
592 lyxerr << "LyX: Child (pid: " << pid
593 << ") stopped on signal "
594 << WSTOPSIG(stat_loc)
595 << ". Waiting for child to finish." << endl;
598 lyxerr << "LyX: Something rotten happened while "
599 "waiting for child " << pid << endl;
601 // Child died, so pretend it returned 1
602 actCall->setRetValue(1);
608 forkedCalls.erase(it);
609 actCall->emitSignal();
611 /* start all over: emiting the signal can result
612 * in changing the list (Ab)
614 it = forkedCalls.begin();
622 // Kill the process prematurely and remove it from the list
623 // within tolerance secs
624 void kill(pid_t pid, int tolerance)
626 ListType::iterator it = find_pid(pid);
627 if (it == forkedCalls.end())
630 (*it)->kill(tolerance);
631 forkedCalls.erase(it);
634 } // namespace ForkedCallsController
636 } // namespace support