Legends-of-Azeroth-Pandaria-5.4.8

Форк
0
280 строк · 8.1 Кб
1
/*
2
* This file is part of the Pandaria 5.4.8 Project. See THANKS 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 "Optional.h"
22

23
#include "Hacks/boost_1_73_process_windows_nopch.h"
24

25
#include <boost/algorithm/string/join.hpp>
26
#include <boost/iostreams/copy.hpp>
27
#include <boost/process/args.hpp>
28
#include <boost/process/child.hpp>
29
#include <boost/process/env.hpp>
30
#include <boost/process/exe.hpp>
31
#include <boost/process/io.hpp>
32
#include <boost/process/pipe.hpp>
33
#include <boost/process/search_path.hpp>
34

35
using namespace boost::process;
36
using namespace boost::iostreams;
37

38
namespace Trinity
39
{
40

41
template<typename T>
42
class TCLogSink
43
{
44
    T callback_;
45

46
public:
47
    typedef char      char_type;
48
    typedef sink_tag  category;
49

50
    // Requires a callback type which has a void(std::string) signature
51
    TCLogSink(T callback)
52
        : callback_(std::move(callback)) { }
53

54
    std::streamsize write(char const* str, std::streamsize size)
55
    {
56
        std::string_view consoleStr(str, size);
57
        size_t lineEnd = consoleStr.find_first_of("\r\n");
58
        std::streamsize processedCharacters = size;
59
        if (lineEnd != std::string_view::npos)
60
        {
61
            consoleStr = consoleStr.substr(0, lineEnd);
62
            processedCharacters = lineEnd + 1;
63
        }
64

65
        if (!consoleStr.empty())
66
            callback_(consoleStr);
67

68
        return processedCharacters;
69
    }
70
};
71

72
template<typename T>
73
auto MakeTCLogSink(T&& callback)
74
    -> TCLogSink<typename std::decay<T>::type>
75
{
76
    return { std::forward<T>(callback) };
77
}
78

79
template<typename T>
80
static int CreateChildProcess(T waiter, std::string const& executable,
81
                              std::vector<std::string> const& argsVector,
82
                              std::string const& logger, std::string const& input,
83
                              bool secure)
84
{
85
    ipstream outStream;
86
    ipstream errStream;
87

88
    if (!secure)
89
    {
90
        TC_LOG_TRACE(logger, "Starting process \"%s\" with arguments: \"%s\".",
91
                executable.c_str(), boost::algorithm::join(argsVector, " ").c_str());
92
    }
93

94
    // prepare file with only read permission (boost process opens with read_write)
95
    std::shared_ptr<FILE> inputFile(!input.empty() ? fopen(input.c_str(), "rb") : nullptr, [](FILE* ptr)
96
    {
97
        if (ptr != nullptr)
98
            fclose(ptr);
99
    });
100

101
    // Start the child process
102
    child c = [&]()
103
    {
104
        if (inputFile)
105
        {
106
            // With binding stdin
107
            return child{
108
                exe = boost::filesystem::absolute(executable).string(),
109
                args = argsVector,
110
                env = environment(boost::this_process::environment()),
111
                std_in = inputFile.get(),
112
                std_out = outStream,
113
                std_err = errStream
114
            };
115
        }
116
        else
117
        {
118
            // Without binding stdin
119
            return child{
120
                exe = boost::filesystem::absolute(executable).string(),
121
                args = argsVector,
122
                env = environment(boost::this_process::environment()),
123
                std_in = boost::process::close,
124
                std_out = outStream,
125
                std_err = errStream
126
            };
127
        }
128
    }();
129

130
    auto outInfo = MakeTCLogSink([&](std::string_view msg)
131
    {
132
        TC_LOG_INFO(logger, STRING_VIEW_FMT, STRING_VIEW_FMT_ARG(msg));
133
    });
134

135
    auto outError = MakeTCLogSink([&](std::string_view msg)
136
    {
137
        TC_LOG_ERROR(logger, STRING_VIEW_FMT, STRING_VIEW_FMT_ARG(msg));
138
    });
139

140
    copy(outStream, outInfo);
141
    copy(errStream, outError);
142

143
    // Call the waiter in the current scope to prevent
144
    // the streams from closing too early on leaving the scope.
145
    int const result = waiter(c);
146

147
    if (!secure)
148
    {
149
        TC_LOG_TRACE(logger, ">> Process \"%s\" finished with return value %i.",
150
                executable.c_str(), result);
151
    }
152

153
    return result;
154
}
155

156
int StartProcess(std::string const& executable, std::vector<std::string> const& args,
157
                 std::string const& logger, std::string input_file, bool secure)
158
{
159
    return CreateChildProcess([](child& c) -> int
160
    {
161
        try
162
        {
163
            c.wait();
164
            return c.exit_code();
165
        }
166
        catch (...)
167
        {
168
            return EXIT_FAILURE;
169
        }
170
    }, executable, args, logger, input_file, secure);
171
}
172

173
class AsyncProcessResultImplementation
174
    : public AsyncProcessResult
175
{
176
    std::string const executable;
177
    std::vector<std::string> const args;
178
    std::string const logger;
179
    std::string const input_file;
180
    bool const is_secure;
181

182
    std::atomic<bool> was_terminated;
183

184
    // Workaround for missing move support in boost < 1.57
185
    Optional<std::shared_ptr<std::future<int>>> result;
186
    Optional<std::reference_wrapper<child>> my_child;
187

188
public:
189
    explicit AsyncProcessResultImplementation(std::string executable_, std::vector<std::string> args_,
190
                                     std::string logger_, std::string input_file_,
191
                                     bool secure)
192
        : executable(std::move(executable_)), args(std::move(args_)),
193
          logger(std::move(logger_)), input_file(input_file_),
194
          is_secure(secure), was_terminated(false) { }
195

196
    AsyncProcessResultImplementation(AsyncProcessResultImplementation const&) = delete;
197
    AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation const&) = delete;
198
    AsyncProcessResultImplementation(AsyncProcessResultImplementation&&) = delete;
199
    AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation&&) = delete;
200

201
    int StartProcess()
202
    {
203
        ASSERT(!my_child, "Process started already!");
204

205
        return CreateChildProcess([&](child& c) -> int
206
        {
207
            int result;
208
            my_child = std::reference_wrapper<child>(c);
209

210
            try
211
            {
212
                c.wait();
213
                result = c.exit_code();
214
            }
215
            catch (...)
216
            {
217
                result = EXIT_FAILURE;
218
            }
219

220
            my_child.reset();
221
            return was_terminated ? EXIT_FAILURE : result;
222

223
        }, executable, args, logger, input_file, is_secure);
224
    }
225

226
    void SetFuture(std::future<int> result_)
227
    {
228
        result = std::make_shared<std::future<int>>(std::move(result_));
229
    }
230

231
    /// Returns the future which contains the result of the process
232
    /// as soon it is finished.
233
    std::future<int>& GetFutureResult() override
234
    {
235
        ASSERT(*result, "The process wasn't started!");
236
        return **result;
237
    }
238

239
    /// Tries to terminate the process
240
    void Terminate() override
241
    {
242
        if (my_child)
243
        {
244
            was_terminated = true;
245
            try
246
            {
247
                my_child->get().terminate();
248
            }
249
            catch(...)
250
            {
251
                // Do nothing
252
            }
253
        }
254
    }
255
};
256

257
std::shared_ptr<AsyncProcessResult>
258
    StartAsyncProcess(std::string executable, std::vector<std::string> args,
259
                      std::string logger, std::string input_file, bool secure)
260
{
261
    auto handle = std::make_shared<AsyncProcessResultImplementation>(
262
        std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure);
263

264
    handle->SetFuture(std::async(std::launch::async, [handle] { return handle->StartProcess(); }));
265
    return handle;
266
}
267

268
std::string SearchExecutableInPath(std::string const& filename)
269
{
270
    try
271
    {
272
        return search_path(filename).string();
273
    }
274
    catch (...)
275
    {
276
        return "";
277
    }
278
}
279

280
} // namespace Trinity
281

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.