TrinityCore
Loading...
Searching...
No Matches
StartProcess.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18// compatibility for booost 1.74 (no boost/process/v1/) and 1.88 (no boost/process/)
19#if __has_include(<boost/process/v1/args.hpp>)
20#define BOOST_PROCESS_V1_HEADER(header) <boost/process/v1/header>
21#define BOOST_PROCESS_VERSION 1
22#else
23#define BOOST_PROCESS_V1_HEADER(header) <boost/process/header>
24#endif
25
26#include "StartProcess.h"
27#include "Errors.h"
28#include "Log.h"
29#include "Optional.h"
30#include BOOST_PROCESS_V1_HEADER(args.hpp)
31#include BOOST_PROCESS_V1_HEADER(child.hpp)
32#include BOOST_PROCESS_V1_HEADER(env.hpp)
33#include BOOST_PROCESS_V1_HEADER(error.hpp)
34#include BOOST_PROCESS_V1_HEADER(exe.hpp)
35#include BOOST_PROCESS_V1_HEADER(io.hpp)
36#include BOOST_PROCESS_V1_HEADER(pipe.hpp)
37#include BOOST_PROCESS_V1_HEADER(search_path.hpp)
38#include <fmt/ranges.h>
39
40namespace bp = boost::process;
41
42namespace Trinity
43{
45 : public AsyncProcessResult
46{
47 std::string const executable;
48 std::vector<std::string> const args;
49 std::string const logger;
50 std::string const input_file;
51 bool const is_secure;
52
53 std::atomic<bool> was_terminated;
54
57
58public:
59 explicit AsyncProcessResultImplementation(std::string executable_, std::vector<std::string> args_,
60 std::string logger_, std::string input_file_,
61 bool secure)
62 : executable(std::move(executable_)), args(std::move(args_)),
63 logger(std::move(logger_)), input_file(std::move(input_file_)),
64 is_secure(secure), was_terminated(false) { }
65
70
72
74 {
75 ASSERT(!my_child, "Process started already!");
76
77#if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT
78#pragma warning(push)
79#pragma warning(disable:4297)
80/*
81 Silence warning with boost 1.83
82
83 boost/process/pipe.hpp(132,5): warning C4297: 'boost::process::basic_pipebuf<char,std::char_traits<char>>::~basic_pipebuf': function assumed not to throw an exception but does
84 boost/process/pipe.hpp(132,5): message : destructor or deallocator has a (possibly implicit) non-throwing exception specification
85 boost/process/pipe.hpp(124,6): message : while compiling class template member function 'boost::process::basic_pipebuf<char,std::char_traits<char>>::~basic_pipebuf(void)'
86 boost/process/pipe.hpp(304,42): message : see reference to class template instantiation 'boost::process::basic_pipebuf<char,std::char_traits<char>>' being compiled
87*/
88#endif
89 bp::ipstream outStream;
90 bp::ipstream errStream;
91#if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT
92#pragma warning(pop)
93#endif
94
95 if (is_secure)
96 {
97 TC_LOG_TRACE(logger, R"(Starting process "{}".)",
99 }
100 else
101 {
102 TC_LOG_TRACE(logger, R"(Starting process "{}" with arguments: "{}".)",
103 executable, fmt::join(args, " "));
104 }
105
106 // prepare file with only read permission (boost process opens with read_write)
107 auto inputFile = std::shared_ptr<FILE>(!input_file.empty() ? fopen(input_file.c_str(), "rb") : nullptr, [](FILE* f) { if (f) fclose(f); });
108
109 std::error_code ec;
110
111 // Start the child process
112 if (inputFile)
113 {
114 my_child.emplace(
115 bp::exe = boost::filesystem::absolute(executable).string(),
116 bp::args = args,
117 bp::env = bp::environment(boost::this_process::environment()),
118 bp::std_in = inputFile.get(),
119 bp::std_out = outStream,
120 bp::std_err = errStream,
121 bp::error = ec
122 );
123 }
124 else
125 {
126 my_child.emplace(
127 bp::exe = boost::filesystem::absolute(executable).string(),
128 bp::args = args,
129 bp::env = bp::environment(boost::this_process::environment()),
130 bp::std_in = bp::close,
131 bp::std_out = outStream,
132 bp::std_err = errStream,
133 bp::error = ec
134 );
135 }
136
137 if (ec)
138 {
139 TC_LOG_ERROR(logger, R"(>> Failed to start process "{}": {})", executable, ec.message());
140 return EXIT_FAILURE;
141 }
142
143 std::future<void> stdOutReader = std::async(std::launch::async, [&]
144 {
145 std::string line;
146 while (std::getline(outStream, line, '\n'))
147 {
148 std::erase(line, '\r');
149 if (!line.empty())
150 TC_LOG_INFO(logger, "{}", line);
151 }
152 });
153
154 std::future<void> stdErrReader = std::async(std::launch::async, [&]
155 {
156 std::string line;
157 while (std::getline(errStream, line, '\n'))
158 {
159 std::erase(line, '\r');
160 if (!line.empty())
161 TC_LOG_ERROR(logger, "{}", line);
162 }
163 });
164
165 my_child->wait(ec);
166 int32 const result = !ec && !was_terminated ? my_child->exit_code() : EXIT_FAILURE;
167 my_child.reset();
168
169 stdOutReader.wait();
170 stdErrReader.wait();
171
172 TC_LOG_TRACE(logger, R"(>> Process "{}" finished with return value {}.)",
173 executable, result);
174
175 return result;
176 }
177
178 void SetFuture(std::future<int32> result_)
179 {
180 futureResult.emplace(std::move(result_));
181 }
182
185 std::future<int32>& GetFutureResult() override
186 {
187 ASSERT(futureResult.has_value(), "The process wasn't started!");
188 return *futureResult;
189 }
190
192 void Terminate() override
193 {
194 if (my_child)
195 {
196 was_terminated = true;
197 std::error_code ec;
198 my_child->terminate(ec);
199 }
200 }
201};
202
203int32 StartProcess(std::string executable, std::vector<std::string> args,
204 std::string logger, std::string input_file, bool secure)
205{
207 std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure);
208
209 return handle.StartProcess();
210}
211
212std::shared_ptr<AsyncProcessResult> StartAsyncProcess(std::string executable, std::vector<std::string> args,
213 std::string logger, std::string input_file, bool secure)
214{
215 std::shared_ptr<AsyncProcessResultImplementation> handle = std::make_shared<AsyncProcessResultImplementation>(
216 std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure);
217
218 handle->SetFuture(std::async(std::launch::async, [handle] { return handle->StartProcess(); }));
219 return handle;
220}
221
222std::string SearchExecutableInPath(std::string const& filename)
223{
224 try
225 {
226 return bp::search_path(filename).string();
227 }
228 catch (...)
229 {
230 return "";
231 }
232}
233
234} // namespace Trinity
int32_t int32
Definition Define.h:129
#define ASSERT
Definition Errors.h:68
#define TC_LOG_TRACE(filterType__,...)
Definition Log.h:153
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition Log.h:159
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
void SetFuture(std::future< int32 > result_)
AsyncProcessResultImplementation & operator=(AsyncProcessResultImplementation const &)=delete
std::vector< std::string > const args
AsyncProcessResultImplementation(AsyncProcessResultImplementation &&)=delete
AsyncProcessResultImplementation(AsyncProcessResultImplementation const &)=delete
void Terminate() override
Tries to terminate the process.
Optional< std::future< int > > futureResult
AsyncProcessResultImplementation(std::string executable_, std::vector< std::string > args_, std::string logger_, std::string input_file_, bool secure)
std::future< int32 > & GetFutureResult() override
std::shared_ptr< AsyncProcessResult > StartAsyncProcess(std::string executable, std::vector< std::string > args, std::string logger, std::string input_file, bool secure)
std::string SearchExecutableInPath(std::string const &filename)
int32 StartProcess(std::string executable, std::vector< std::string > args, std::string logger, std::string input_file, bool secure)
STL namespace.