/* * Copyright (C) 2008-2016 TrinityCore * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "StartProcess.h" #include #include #include #include #include #include #include #include #include #include #include "Common.h" #include "Log.h" using namespace boost::process; using namespace boost::process::initializers; using namespace boost::iostreams; namespace Trinity { template class TCLogSink { T callback_; public: typedef char char_type; typedef sink_tag category; // Requires a callback type which has a void(std::string) signature TCLogSink(T callback) : callback_(std::move(callback)) { } std::streamsize write(const char* str, std::streamsize size) { callback_(std::string(str, size)); return size; } }; template auto MakeTCLogSink(T&& callback) -> TCLogSink::type> { return { std::forward(callback) }; } template static int CreateChildProcess(T waiter, std::string const& executable, std::vector const& args, std::string const& logger, std::string const& input, bool secure) { auto outPipe = create_pipe(); auto errPipe = create_pipe(); Optional inputSource; if (!secure) { TC_LOG_TRACE(logger, "Starting process \"%s\" with arguments: \"%s\".", executable.c_str(), boost::algorithm::join(args, " ").c_str()); } // Start the child process child c = [&] { if (!input.empty()) { inputSource = file_descriptor_source(input); // With binding stdin return execute(run_exe(boost::filesystem::absolute(executable)), set_args(args), inherit_env(), bind_stdin(*inputSource), bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)), bind_stderr(file_descriptor_sink(errPipe.sink, close_handle))); } else { // Without binding stdin return execute(run_exe(boost::filesystem::absolute(executable)), set_args(args), inherit_env(), bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)), bind_stderr(file_descriptor_sink(errPipe.sink, close_handle))); } }(); file_descriptor_source outFd(outPipe.source, close_handle); file_descriptor_source errFd(errPipe.source, close_handle); auto outInfo = MakeTCLogSink([&](std::string msg) { TC_LOG_INFO(logger, "%s", msg.c_str()); }); auto outError = MakeTCLogSink([&](std::string msg) { TC_LOG_ERROR(logger, "%s", msg.c_str()); }); copy(outFd, outInfo); copy(errFd, outError); // Call the waiter in the current scope to prevent // the streams from closing too early on leaving the scope. int const result = waiter(c); if (!secure) { TC_LOG_TRACE(logger, ">> Process \"%s\" finished with return value %i.", executable.c_str(), result); } if (inputSource) inputSource->close(); return result; } int StartProcess(std::string const& executable, std::vector const& args, std::string const& logger, std::string input_file, bool secure) { return CreateChildProcess([](child& c) -> int { try { return wait_for_exit(c); } catch (...) { return EXIT_FAILURE; } }, executable, args, logger, input_file, secure); } class AsyncProcessResultImplementation : public AsyncProcessResult { std::string const executable; std::vector const args; std::string const logger; std::string const input_file; bool const is_secure; std::atomic was_terminated; // Workaround for missing move support in boost < 1.57 Optional>> result; Optional> my_child; public: explicit AsyncProcessResultImplementation(std::string executable_, std::vector args_, std::string logger_, std::string input_file_, bool secure) : executable(std::move(executable_)), args(std::move(args_)), logger(std::move(logger_)), input_file(input_file_), is_secure(secure), was_terminated(false) { } AsyncProcessResultImplementation(AsyncProcessResultImplementation const&) = delete; AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation const&) = delete; AsyncProcessResultImplementation(AsyncProcessResultImplementation&&) = delete; AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation&&) = delete; int StartProcess() { ASSERT(!my_child, "Process started already!"); return CreateChildProcess([&](child& c) -> int { int result; my_child = std::reference_wrapper(c); try { result = wait_for_exit(c); } catch (...) { result = EXIT_FAILURE; } my_child.reset(); return was_terminated ? EXIT_FAILURE : result; }, executable, args, logger, input_file, is_secure); } void SetFuture(std::future result_) { result = std::make_shared>(std::move(result_)); } /// Returns the future which contains the result of the process /// as soon it is finished. std::future& GetFutureResult() override { ASSERT(*result, "The process wasn't started!"); return **result; } /// Tries to terminate the process void Terminate() override { if (!my_child) { was_terminated = true; try { terminate(my_child->get()); } catch(...) { // Do nothing } } } }; std::shared_ptr StartAsyncProcess(std::string executable, std::vector args, std::string logger, std::string input_file, bool secure) { auto handle = std::make_shared( std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure); handle->SetFuture(std::async(std::launch::async, [handle] { return handle->StartProcess(); })); return handle; } Optional SearchExecutableInPath(std::string const& filename) { try { auto result = search_path(filename); if (result.empty()) return boost::none; else return result; } catch (...) { return boost::none; } } } // namespace Trinity