diff --git a/src/meson.build b/src/meson.build index f2be42e29..ab0760099 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,6 +6,7 @@ kiwix_sources = [ 'reader.cpp', 'entry.cpp', 'searcher.cpp', + 'subprocess.cpp', 'common/base64.cpp', 'common/pathTools.cpp', 'common/regexTools.cpp', @@ -17,6 +18,12 @@ kiwix_sources = [ ] kiwix_sources += lib_resources +if host_machine.system() == 'windows' + kiwix_sources += 'subprocess_windows.cpp' +else + kiwix_sources += 'subprocess_unix.cpp' +endif + if xapian_dep.found() kiwix_sources += ['xapianSearcher.cpp'] endif diff --git a/src/subprocess.cpp b/src/subprocess.cpp new file mode 100644 index 000000000..6c958c489 --- /dev/null +++ b/src/subprocess.cpp @@ -0,0 +1,40 @@ + + +#include "subprocess.h" + +#ifdef _WIN32 +# include "subprocess_windows.h" +#else +# include "subprocess_unix.h" +#endif + +Subprocess::Subprocess(std::unique_ptr impl, const commandLine_t& commandLine) : + mp_impl(std::move(impl)) +{ + mp_impl->run(commandLine); +} + +Subprocess::~Subprocess() +{ + mp_impl->kill(); +} + +std::unique_ptr Subprocess::run(const commandLine_t& commandLine) +{ +#ifdef _WIN32 + auto impl = std::unique_ptr(new WinImpl); +#else + auto impl = std::unique_ptr(new UnixImpl); +#endif + return std::unique_ptr(new Subprocess(std::move(impl), commandLine)); +} + +bool Subprocess::isRunning() +{ + return mp_impl->isRunning(); +} + +bool Subprocess::kill() +{ + return mp_impl->kill(); +} diff --git a/src/subprocess.h b/src/subprocess.h new file mode 100644 index 000000000..9ed75eb21 --- /dev/null +++ b/src/subprocess.h @@ -0,0 +1,36 @@ + +#ifndef KIWIX_SUBPROCESS_H_ +#define KIWIX_SUBPROCESS_H_ + +#include +#include +#include + +typedef std::vector commandLine_t; + +class SubprocessImpl +{ + public: + virtual void run(const commandLine_t& commandLine) = 0; + virtual bool kill() = 0; + virtual bool isRunning() = 0; + virtual ~SubprocessImpl() = default; +}; + +class Subprocess +{ + private: + // Impl depends of the system (window, unix, ...) + std::unique_ptr mp_impl; + Subprocess(std::unique_ptr impl, const commandLine_t& commandLine); + + public: + static std::unique_ptr run(const commandLine_t& commandLine); + ~Subprocess(); + + bool isRunning(); + bool kill(); +}; + + +#endif // KIWIX_SUBPROCESS_H_ diff --git a/src/subprocess_unix.cpp b/src/subprocess_unix.cpp new file mode 100644 index 000000000..8c5272816 --- /dev/null +++ b/src/subprocess_unix.cpp @@ -0,0 +1,72 @@ + + +#include "subprocess_unix.h" + +#include +#include +#include +#include +#include + +UnixImpl::UnixImpl(): + m_pid(0), + m_running(false), + m_mutex(PTHREAD_MUTEX_INITIALIZER), + m_waitingThread() +{ +} + +UnixImpl::~UnixImpl() +{ + kill(); + pthread_cancel(m_waitingThread); +} + +void* UnixImpl::waitForPID(void* _self) +{ + UnixImpl* self = static_cast(_self); + waitpid(self->m_pid, NULL, WEXITED); + + pthread_mutex_lock(&self->m_mutex); + self->m_running = false; + pthread_mutex_unlock(&self->m_mutex); + + return self; +} + +void UnixImpl::run(const commandLine_t& commandLine) +{ + const char* binary = commandLine[0]; + std::cerr << "running " << binary << std::endl; + int pid = fork(); + switch(pid) { + case -1: + std::cerr << "cannot fork" << std::endl; + break; + case 0: + if (execvp(binary, const_cast(commandLine.data()))) { + perror("Cannot launch\n"); + exit(-1); + } + + break; + default: + m_pid = pid; + m_running = true; + pthread_create(&m_waitingThread, NULL, waitForPID, this); + break; + } +} + +bool UnixImpl::kill() +{ + return (::kill(m_pid, SIGKILL) == 0); +} + +bool UnixImpl::isRunning() +{ + pthread_mutex_lock(&m_mutex); + bool ret = m_running; + pthread_mutex_unlock(&m_mutex); + return ret; +} diff --git a/src/subprocess_unix.h b/src/subprocess_unix.h new file mode 100644 index 000000000..750658943 --- /dev/null +++ b/src/subprocess_unix.h @@ -0,0 +1,28 @@ +#ifndef KIWIX_SUBPROCESS_UNIX_H_ +#define KIWIX_SUBPROCESS_UNIX_H_ + +#include "subprocess.h" + +#include + + +class UnixImpl : public SubprocessImpl +{ + private: + int m_pid; + bool m_running; + pthread_mutex_t m_mutex; + pthread_t m_waitingThread; + + public: + UnixImpl(); + virtual ~UnixImpl(); + + void run(const commandLine_t& commandLine); + bool kill(); + bool isRunning(); + + static void* waitForPID(void* self); +}; + +#endif //KIWIX_SUBPROCESS_UNIX_H_ diff --git a/src/subprocess_windows.cpp b/src/subprocess_windows.cpp new file mode 100644 index 000000000..226bd5d15 --- /dev/null +++ b/src/subprocess_windows.cpp @@ -0,0 +1,94 @@ + + +#include "subprocess_windows.h" + +#include +#include +#include +#include + +WinImpl::WinImpl(): + m_pid(0), + m_running(false), + m_handle(INVALID_HANDLE_VALUE) +{ + InitializeCriticalSection(&m_criticalSection); +} + +WinImpl::~WinImpl() +{ + kill(); + CloseHandle(m_handle); + DeleteCriticalSection(&m_criticalSection); +} + +DWORD WINAPI WinImpl::waitForPID(void* _self) +{ + WinImpl* self = static_cast(_self); + WaitForSingleObject(self->m_handle, INFINITE); + + EnterCriticalSection(&self->m_criticalSection); + self->m_running = false; + LeaveCriticalSection(&self->m_criticalSection); + + return 0; +} + +std::unique_ptr toWideChar(const std::string& value) +{ + auto size = MultiByteToWideChar(CP_UTF8, 0, + value.c_str(), -1, nullptr, 0); + auto wdata = std::unique_ptr(new wchar_t[size]); + auto ret = MultiByteToWideChar(CP_UTF8, 0, + value.c_str(), -1, wdata.get(), size); + if (0 == ret) { + std::ostringstream oss; + oss << "Cannot convert to wchar : " << GetLastError(); + throw std::runtime_error(oss.str()); + } + return wdata; +} + + +void WinImpl::run(const commandLine_t& commandLine) +{ + STARTUPINFOW startInfo = {0}; + PROCESS_INFORMATION procInfo; + startInfo.cb = sizeof(startInfo); + const char* binary = commandLine[0]; + std::cerr << "running " << binary << std::endl; + std::ostringstream oss; + for(auto& item: commandLine) { + oss << item << " "; + } + if (CreateProcessW( + toWideChar(binary).get(), + toWideChar(oss.str()).get(), + NULL, + NULL, + false, + CREATE_NO_WINDOW, + NULL, + NULL, + &startInfo, + &procInfo)) { + m_pid = procInfo.dwProcessId; + m_handle = procInfo.hProcess; + CloseHandle(procInfo.hThread); + m_running = true; + CreateThread(NULL, 0, &waitForPID, this, 0, NULL ); + } +} + +bool WinImpl::kill() +{ + return TerminateProcess(m_handle, 0); +} + +bool WinImpl::isRunning() +{ + EnterCriticalSection(&m_criticalSection); + bool ret = m_running; + LeaveCriticalSection(&m_criticalSection); + return ret; +} diff --git a/src/subprocess_windows.h b/src/subprocess_windows.h new file mode 100644 index 000000000..42aaa41f5 --- /dev/null +++ b/src/subprocess_windows.h @@ -0,0 +1,27 @@ +#ifndef KIWIX_SUBPROCESS_WINDOWS_H_ +#define KIWIX_SUBPROCESS_WINDOWS_H_ + +#include "subprocess.h" + +#include + +class WinImpl : public SubprocessImpl +{ + private: + int m_pid; + bool m_running; + HANDLE m_handle; + CRITICAL_SECTION m_criticalSection; + + public: + WinImpl(); + virtual ~WinImpl(); + + void run(const commandLine_t& commandLine); + bool kill(); + bool isRunning(); + + static DWORD WINAPI waitForPID(void* self); +}; + +#endif //KIWIX_SUBPROCESS_WINDOWS_H_