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 "Memory.h"
30#include "Optional.h"
31#include BOOST_PROCESS_V1_HEADER(args.hpp)
32#include BOOST_PROCESS_V1_HEADER(child.hpp)
33#include BOOST_PROCESS_V1_HEADER(env.hpp)
34#include BOOST_PROCESS_V1_HEADER(error.hpp)
35#include BOOST_PROCESS_V1_HEADER(exe.hpp)
36#include BOOST_PROCESS_V1_HEADER(io.hpp)
37#include BOOST_PROCESS_V1_HEADER(pipe.hpp)
38#include BOOST_PROCESS_V1_HEADER(search_path.hpp)
39#include <fmt/ranges.h>
40
41namespace bp = boost::process;
42
43namespace Trinity
44{
46 : public AsyncProcessResult
47{
48 std::string const executable;
49 std::vector<std::string> const args;
50 std::string const logger;
51 std::string const input_file;
52 bool const is_secure;
53
54 std::atomic<bool> was_terminated;
55
58
59public:
60 explicit AsyncProcessResultImplementation(std::string executable_, std::vector<std::string> args_,
61 std::string logger_, std::string input_file_,
62 bool secure)
63 : executable(std::move(executable_)), args(std::move(args_)),
64 logger(std::move(logger_)), input_file(std::move(input_file_)),
65 is_secure(secure), was_terminated(false) { }
66
71
73
75 {
76 ASSERT(!my_child, "Process started already!");
77
78#if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT
79#pragma warning(push)
80#pragma warning(disable:4297)
81/*
82 Silence warning with boost 1.83
83
84 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
85 boost/process/pipe.hpp(132,5): message : destructor or deallocator has a (possibly implicit) non-throwing exception specification
86 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)'
87 boost/process/pipe.hpp(304,42): message : see reference to class template instantiation 'boost::process::basic_pipebuf<char,std::char_traits<char>>' being compiled
88*/
89#endif
90 bp::ipstream outStream;
91 bp::ipstream errStream;
92#if TRINITY_COMPILER == TRINITY_COMPILER_MICROSOFT
93#pragma warning(pop)
94#endif
95
96 if (is_secure)
97 {
98 TC_LOG_TRACE(logger, R"(Starting process "{}".)",
100 }
101 else
102 {
103 TC_LOG_TRACE(logger, R"(Starting process "{}" with arguments: "{}".)",
104 executable, fmt::join(args, " "));
105 }
106
107 // prepare file with only read permission (boost process opens with read_write)
108 auto inputFile = Trinity::make_unique_ptr_with_deleter<&::fclose>(!input_file.empty() ? fopen(input_file.c_str(), "rb") : nullptr);
109
110 std::error_code ec;
111
112 // Start the child process
113 if (inputFile)
114 {
115 my_child.emplace(
116 bp::exe = boost::filesystem::absolute(executable).string(),
117 bp::args = args,
118 bp::env = bp::environment(boost::this_process::environment()),
119 bp::std_in = inputFile.get(),
120 bp::std_out = outStream,
121 bp::std_err = errStream,
122 bp::error = ec
123 );
124 }
125 else
126 {
127 my_child.emplace(
128 bp::exe = boost::filesystem::absolute(executable).string(),
129 bp::args = args,
130 bp::env = bp::environment(boost::this_process::environment()),
131 bp::std_in = bp::close,
132 bp::std_out = outStream,
133 bp::std_err = errStream,
134 bp::error = ec
135 );
136 }
137
138 if (ec)
139 {
140 TC_LOG_ERROR(logger, R"(>> Failed to start process "{}": {})", executable, ec.message());
141 return EXIT_FAILURE;
142 }
143
144 std::future<void> stdOutReader = std::async(std::launch::async, [&]
145 {
146 std::string line;
147 while (std::getline(outStream, line, '\n'))
148 {
149 std::erase(line, '\r');
150 if (!line.empty())
151 TC_LOG_INFO(logger, "{}", line);
152 }
153 });
154
155 std::future<void> stdErrReader = std::async(std::launch::async, [&]
156 {
157 std::string line;
158 while (std::getline(errStream, line, '\n'))
159 {
160 std::erase(line, '\r');
161 if (!line.empty())
162 TC_LOG_ERROR(logger, "{}", line);
163 }
164 });
165
166 my_child->wait(ec);
167 int32 const result = !ec && !was_terminated ? my_child->exit_code() : EXIT_FAILURE;
168 my_child.reset();
169
170 stdOutReader.wait();
171 stdErrReader.wait();
172
173 TC_LOG_TRACE(logger, R"(>> Process "{}" finished with return value {}.)",
174 executable, result);
175
176 return result;
177 }
178
179 void SetFuture(std::future<int32> result_)
180 {
181 futureResult.emplace(std::move(result_));
182 }
183
186 std::future<int32>& GetFutureResult() override
187 {
188 ASSERT(futureResult.has_value(), "The process wasn't started!");
189 return *futureResult;
190 }
191
193 void Terminate() override
194 {
195 if (my_child)
196 {
197 was_terminated = true;
198 std::error_code ec;
199 my_child->terminate(ec);
200 }
201 }
202};
203
204int32 StartProcess(std::string executable, std::vector<std::string> args,
205 std::string logger, std::string input_file, bool secure)
206{
208 std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure);
209
210 return handle.StartProcess();
211}
212
213std::shared_ptr<AsyncProcessResult> StartAsyncProcess(std::string executable, std::vector<std::string> args,
214 std::string logger, std::string input_file, bool secure)
215{
216 std::shared_ptr<AsyncProcessResultImplementation> handle = std::make_shared<AsyncProcessResultImplementation>(
217 std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure);
218
219 handle->SetFuture(std::async(std::launch::async, [handle] { return handle->StartProcess(); }));
220 return handle;
221}
222
223std::string SearchExecutableInPath(std::string const& filename)
224{
225 try
226 {
227 return bp::search_path(filename).string();
228 }
229 catch (...)
230 {
231 return "";
232 }
233}
234
235} // namespace Trinity
int32_t int32
Definition Define.h:150
#define ASSERT
Definition Errors.h:80
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
#define TC_LOG_TRACE(filterType__, message__,...)
Definition Log.h:178
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.