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