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/environment.h"
19 #include "support/filetools.h"
20 #include "support/lstrings.h"
21 #include "support/lyxlib.h"
22 #include "support/os.h"
23 #include "support/Timeout.h"
25 #include "support/bind.h"
47 # include <sys/wait.h>
59 /////////////////////////////////////////////////////////////////////
63 /////////////////////////////////////////////////////////////////////
65 class Murder : public boost::signals::trackable {
68 static void killItDead(int secs, pid_t pid)
71 new Murder(secs, pid);
73 support::kill(pid, SIGKILL);
80 support::kill(pid_, SIGKILL);
81 lyxerr << "Killed " << pid_ << endl;
87 Murder(int secs, pid_t pid)
88 : timeout_(1000*secs, Timeout::ONETIME), pid_(pid)
90 timeout_.timeout.connect(lyx::bind(&Murder::kill, this));
103 /////////////////////////////////////////////////////////////////////
107 /////////////////////////////////////////////////////////////////////
109 ForkedProcess::ForkedProcess()
110 : pid_(0), retval_(0)
114 bool ForkedProcess::IAmAChild = false;
117 void ForkedProcess::emitSignal()
120 signal_->operator()(pid_, retval_);
125 // Spawn the child process
126 int ForkedProcess::run(Starttype type)
129 pid_ = generateChild();
130 if (pid_ <= 0) { // child or fork failed.
133 //we also do this in fork(), too, but maybe someone will try
141 retval_ = waitForChild();
144 // Integrate into the Controller
145 ForkedCallsController::addCall(*this);
154 bool ForkedProcess::running() const
159 #if !defined (_WIN32)
160 // Un-UNIX like, but we don't have much use for
161 // knowing if a zombie exists, so just reap it first.
163 waitpid(pid(), &waitstatus, WNOHANG);
166 // Racy of course, but it will do.
167 if (support::kill(pid(), 0) && errno == ESRCH)
173 void ForkedProcess::kill(int tol)
175 lyxerr << "ForkedProcess::kill(" << tol << ')' << endl;
177 lyxerr << "Can't kill non-existent process!" << endl;
181 int const tolerance = max(0, tol);
182 if (tolerance == 0) {
184 Murder::killItDead(0, pid());
186 int ret = support::kill(pid(), SIGHUP);
188 // The process is already dead if wait_for_death is false
189 bool const wait_for_death = (ret == 0 && errno != ESRCH);
192 Murder::killItDead(tolerance, pid());
197 pid_t ForkedProcess::fork() {
198 #if !defined (HAVE_FORK)
201 pid_t pid = ::fork();
209 // Wait for child process to finish. Returns returncode from child.
210 int ForkedProcess::waitForChild()
212 // We'll pretend that the child returns 1 on all error conditions.
216 HANDLE const hProcess = HANDLE(pid_);
218 DWORD const wait_status = ::WaitForSingleObject(hProcess, INFINITE);
220 switch (wait_status) {
221 case WAIT_OBJECT_0: {
223 if (!GetExitCodeProcess(hProcess, &exit_code)) {
224 lyxerr << "GetExitCodeProcess failed waiting for child\n"
225 << getChildErrorMessage() << endl;
231 lyxerr << "WaitForSingleObject failed waiting for child\n"
232 << getChildErrorMessage() << endl;
240 pid_t waitrpid = waitpid(pid_, &status, WUNTRACED);
241 if (waitrpid == -1) {
242 lyxerr << "LyX: Error waiting for child:"
243 << strerror(errno) << endl;
245 } else if (WIFEXITED(status)) {
246 // Child exited normally. Update return value.
247 retval_ = WEXITSTATUS(status);
249 } else if (WIFSIGNALED(status)) {
250 lyxerr << "LyX: Child didn't catch signal "
252 << "and died. Too bad." << endl;
254 } else if (WIFSTOPPED(status)) {
255 lyxerr << "LyX: Child (pid: " << pid_
256 << ") stopped on signal "
258 << ". Waiting for child to finish." << endl;
260 lyxerr << "LyX: Something rotten happened while "
261 "waiting for child " << pid_ << endl;
270 /////////////////////////////////////////////////////////////////////
274 /////////////////////////////////////////////////////////////////////
276 ForkedCall::ForkedCall(string const & path)
277 : cmd_prefix_(empty_string())
279 if (path.empty() || lyxrc.texinputs_prefix.empty())
282 string const texinputs = os::latex_path_list(
283 replaceCurdirPath(path, lyxrc.texinputs_prefix));
284 string const sep = string(1, os::path_separator(os::TEXENGINE));
285 string const env = getEnv("TEXINPUTS");
287 if (os::shell() == os::UNIX)
288 cmd_prefix_ = "env 'TEXINPUTS=." + sep + texinputs
291 cmd_prefix_ = "cmd /d /c set TEXINPUTS=." + sep + texinputs
296 int ForkedCall::startScript(Starttype wait, string const & what)
299 retval_ = startScript(what, SignalTypePtr());
309 int ForkedCall::startScript(string const & what, SignalTypePtr signal)
314 return run(DontWait);
318 // generate child in background
319 int ForkedCall::generateChild()
321 string const line = trim(cmd_prefix_ + command_);
325 #if !defined (_WIN32)
328 // Split the input command up into an array of words stored
329 // in a contiguous block of memory. The array contains pointers
331 // Don't forget the terminating `\0' character.
332 char const * const c_str = line.c_str();
333 vector<char> vec(c_str, c_str + line.size() + 1);
335 // Splitting the command up into an array of words means replacing
336 // the whitespace between words with '\0'. Life is complicated
337 // however, because words protected by quotes can contain whitespace.
339 // The strategy we adopt is:
340 // 1. If we're not inside quotes, then replace white space with '\0'.
341 // 2. If we are inside quotes, then don't replace the white space
342 // but do remove the quotes themselves. We do this naively by
343 // replacing the quote with '\0' which is fine if quotes
344 // delimit the entire word.
345 char inside_quote = 0;
346 vector<char>::iterator it = vec.begin();
347 vector<char>::iterator const end = vec.end();
348 for (; it != end; ++it) {
353 else if (c == '\'' || c == '"') {
357 } else if (c == inside_quote) {
363 // Build an array of pointers to each word.
367 for (; it != end; ++it) {
368 if (*it != '\0' && prev == '\0')
369 argv.push_back(&*it);
375 if (lyxerr.debugging(Debug::FILES)) {
376 vector<char *>::iterator ait = argv.begin();
377 vector<char *>::iterator const aend = argv.end();
378 lyxerr << "<command>\n\t" << line
379 << "\n\tInterpretted as:\n\n";
380 for (; ait != aend; ++ait)
382 lyxerr << '\t'<< *ait << '\n';
383 lyxerr << "</command>" << endl;
386 pid_t const cpid = ::fork();
389 execvp(argv[0], &*argv.begin());
391 // If something goes wrong, we end up here
392 lyxerr << "execvp of \"" << command_ << "\" failed: "
393 << strerror(errno) << endl;
402 PROCESS_INFORMATION process;
404 memset(&startup, 0, sizeof(STARTUPINFO));
405 memset(&process, 0, sizeof(PROCESS_INFORMATION));
407 startup.cb = sizeof(STARTUPINFO);
409 if (CreateProcess(0, (LPSTR)line.c_str(), 0, 0, FALSE,
410 CREATE_NO_WINDOW, 0, 0, &startup, &process)) {
411 CloseHandle(process.hThread);
412 cpid = (pid_t)process.hProcess;
418 lyxerr << "Could not fork: " << strerror(errno) << endl;
425 /////////////////////////////////////////////////////////////////////
429 /////////////////////////////////////////////////////////////////////
431 namespace ForkedCallQueue {
433 /// A process in the queue
434 typedef pair<string, ForkedCall::SignalTypePtr> Process;
435 /** Add a process to the queue. Processes are forked sequentially
436 * only one is running at a time.
437 * Connect to the returned signal and you'll be informed when
438 * the process has ended.
440 ForkedCall::SignalTypePtr add(string const & process);
442 /// in-progress queue
443 static queue<Process> callQueue_;
445 /// flag whether queue is running
446 static bool running_ = 0;
453 void callback(pid_t, int);
455 ForkedCall::SignalTypePtr add(string const & process)
457 ForkedCall::SignalTypePtr ptr;
458 ptr.reset(new ForkedCall::SignalType);
459 callQueue_.push(Process(process, ptr));
468 if (callQueue_.empty())
470 Process pro = callQueue_.front();
472 // Bind our chain caller
473 pro.second->connect(lyx::bind(&ForkedCallQueue::callback, _1, _2));
475 //If we fail to fork the process, then emit the signal
476 //to tell the outside world that it failed.
477 if (call.startScript(pro.first, pro.second) > 0)
478 pro.second->operator()(0,1);
482 void callback(pid_t, int)
484 if (callQueue_.empty())
493 LYXERR(Debug::GRAPHICS, "ForkedCallQueue: waking up");
502 LYXERR(Debug::GRAPHICS, "ForkedCallQueue: I'm going to sleep");
511 } // namespace ForkedCallsQueue
515 /////////////////////////////////////////////////////////////////////
517 // ForkedCallsController
519 /////////////////////////////////////////////////////////////////////
522 string const getChildErrorMessage()
524 DWORD const error_code = ::GetLastError();
526 HLOCAL t_message = 0;
527 bool const ok = ::FormatMessage(
528 FORMAT_MESSAGE_ALLOCATE_BUFFER |
529 FORMAT_MESSAGE_FROM_SYSTEM,
531 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
532 (LPTSTR) &t_message, 0, 0
536 ss << "LyX: Error waiting for child: " << error_code;
539 ss << ": " << (LPTSTR)t_message;
540 ::LocalFree(t_message);
542 ss << ": Error unknown.";
549 namespace ForkedCallsController {
551 typedef shared_ptr<ForkedProcess> ForkedProcessPtr;
552 typedef list<ForkedProcessPtr> ListType;
553 typedef ListType::iterator iterator;
556 /// The child processes
557 static ListType forkedCalls;
559 iterator find_pid(pid_t pid)
561 return find_if(forkedCalls.begin(), forkedCalls.end(),
562 lyx::bind(equal_to<pid_t>(),
563 lyx::bind(&ForkedCall::pid, _1),
568 void addCall(ForkedProcess const & newcall)
570 forkedCalls.push_back(newcall.clone());
574 // Check the list of dead children and emit any associated signals.
575 void handleCompletedProcesses()
577 ListType::iterator it = forkedCalls.begin();
578 ListType::iterator end = forkedCalls.end();
580 ForkedProcessPtr actCall = *it;
581 bool remove_it = false;
584 HANDLE const hProcess = HANDLE(actCall->pid());
586 DWORD const wait_status = ::WaitForSingleObject(hProcess, 0);
588 switch (wait_status) {
592 case WAIT_OBJECT_0: {
594 if (!GetExitCodeProcess(hProcess, &exit_code)) {
595 lyxerr << "GetExitCodeProcess failed waiting for child\n"
596 << getChildErrorMessage() << endl;
597 // Child died, so pretend it returned 1
598 actCall->setRetValue(1);
600 actCall->setRetValue(exit_code);
602 CloseHandle(hProcess);
607 lyxerr << "WaitForSingleObject failed waiting for child\n"
608 << getChildErrorMessage() << endl;
609 actCall->setRetValue(1);
610 CloseHandle(hProcess);
615 pid_t pid = actCall->pid();
617 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
619 if (waitrpid == -1) {
620 lyxerr << "LyX: Error waiting for child: "
621 << strerror(errno) << endl;
623 // Child died, so pretend it returned 1
624 actCall->setRetValue(1);
627 } else if (waitrpid == 0) {
628 // Still running. Move on to the next child.
630 } else if (WIFEXITED(stat_loc)) {
631 // Ok, the return value goes into retval.
632 actCall->setRetValue(WEXITSTATUS(stat_loc));
635 } else if (WIFSIGNALED(stat_loc)) {
636 // Child died, so pretend it returned 1
637 actCall->setRetValue(1);
640 } else if (WIFSTOPPED(stat_loc)) {
641 lyxerr << "LyX: Child (pid: " << pid
642 << ") stopped on signal "
643 << WSTOPSIG(stat_loc)
644 << ". Waiting for child to finish." << endl;
647 lyxerr << "LyX: Something rotten happened while "
648 "waiting for child " << pid << endl;
650 // Child died, so pretend it returned 1
651 actCall->setRetValue(1);
657 forkedCalls.erase(it);
658 actCall->emitSignal();
660 /* start all over: emiting the signal can result
661 * in changing the list (Ab)
663 it = forkedCalls.begin();
671 // Kill the process prematurely and remove it from the list
672 // within tolerance secs
673 void kill(pid_t pid, int tolerance)
675 ListType::iterator it = find_pid(pid);
676 if (it == forkedCalls.end())
679 (*it)->kill(tolerance);
680 forkedCalls.erase(it);
683 } // namespace ForkedCallsController
685 } // namespace support