MySQL 9.5.0
Source Code Documentation
process_launcher.h
Go to the documentation of this file.
1/* Copyright (c) 2018, 2025, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is designed to work with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have either included with
13 the program or referenced in the documentation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
23
24#ifndef _PROCESS_LAUNCHER_H_
25#define _PROCESS_LAUNCHER_H_
26
27#include <chrono>
28#include <cstdint>
29#include <mutex>
30#include <span>
31#include <stdexcept>
32#include <string>
33#include <system_error>
34#include <utility>
35#include <vector>
36
37#ifdef _WIN32
38#define _CRT_SECURE_NO_WARNINGS 1
39#ifdef UNICODE
40# #undef UNICODE
41#endif
42#include <windows.h>
43#else
44#include <unistd.h>
45#endif
46
47#include "harness_export.h"
48
49#include "exit_status.h"
51
52namespace mysql_harness {
53#ifdef _WIN32
54namespace win32 {
55// reverse of CommandLineToArgv()
56HARNESS_EXPORT std::string cmdline_quote_arg(const std::string &arg);
57HARNESS_EXPORT std::string cmdline_from_args(
58 const std::string &executable_path, const std::vector<std::string> &args);
59
60class HARNESS_EXPORT Handle {
61 public:
62 Handle() = default;
63
64 Handle(HANDLE hndl) : handle_(hndl) {}
65 Handle(const Handle &) = delete;
66 Handle(Handle &&other) : handle_(other.release()) {}
67 Handle &operator=(const Handle &) = delete;
68 Handle &operator=(Handle &&other) {
69 handle_ = other.release();
70 return *this;
71 }
72
73 ~Handle() { close(); }
74
75 bool is_open() const { return handle_ != INVALID_HANDLE_VALUE; }
76
78
79 HANDLE native_handle() const { return handle_; }
80
81 HANDLE release() { return std::exchange(handle_, INVALID_HANDLE_VALUE); }
82
83 static stdx::expected<void, std::error_code> set_information(HANDLE hndl,
84 DWORD mask,
85 DWORD flags);
86
87 stdx::expected<void, std::error_code> set_information(DWORD mask,
88 DWORD flags);
89
90 stdx::expected<DWORD, std::error_code> wait_for_single_object(
91 DWORD timeout_ms);
92
93 private:
94 HANDLE handle_{INVALID_HANDLE_VALUE};
95};
96
97class HARNESS_EXPORT FileHandle : public Handle {
98 public:
99 using Handle::Handle;
100
102 const void *buf, DWORD buf_size, OVERLAPPED *overlapped = nullptr);
103
105 OVERLAPPED *overlapped = nullptr);
106};
107
108class HARNESS_EXPORT PipeHandle : public FileHandle {
109 public:
110 using FileHandle::FileHandle;
111
112 struct PeekResult {
113 DWORD bytesRead;
114 DWORD totalBytesAvail;
115 DWORD bytesLeftThisMessage;
116 };
117
119};
120
121class HARNESS_EXPORT ProcessHandle : public Handle {
122 public:
123 using Handle::Handle;
124
125 stdx::expected<void, std::error_code> terminate(UINT exit_code) const;
126
128};
129
130class HARNESS_EXPORT ThreadHandle : public Handle {
131 public:
133};
134
135class HARNESS_EXPORT JobObject {
136 public:
137 JobObject() = default;
138
139 JobObject(Handle hndl) : handle_(std::move(hndl)) {}
140
142
144 JOBOBJECTINFOCLASS info_class, void *info, DWORD info_size);
145
146 stdx::expected<void, std::error_code> assign_process(HANDLE process);
147
148 bool is_open() const { return handle_.is_open(); }
149
150 private:
151 Handle handle_;
152};
153
154class HARNESS_EXPORT Process {
155 public:
156 Process() = default;
157
158 Process(ProcessHandle process_hndl, ThreadHandle thread_hndl,
159 DWORD process_id, DWORD thread_id)
160 : process_handle_(std::move(process_hndl)),
161 thread_handle_(std::move(thread_hndl)),
162 process_id_(process_id),
163 thread_id_(thread_id) {}
164
166 const char *app_name, char *cmd_line, SECURITY_ATTRIBUTES *process_attrs,
167 SECURITY_ATTRIBUTES *thread_attrs, BOOL inherit_handles,
168 DWORD creation_flags, void *env, const char *current_dir,
169 STARTUPINFO *startup_info);
170
171 ProcessHandle &process_handle() { return process_handle_; }
172 const ProcessHandle &process_handle() const { return process_handle_; }
173
174 ThreadHandle &thread_handle() { return thread_handle_; }
175 const ThreadHandle &thread_handle() const { return thread_handle_; }
176
177 DWORD process_id() const { return process_id_; }
178 DWORD thread_id() const { return thread_id_; }
179
180 private:
181 ProcessHandle process_handle_;
182 ThreadHandle thread_handle_;
183 DWORD process_id_{};
184 DWORD thread_id_{};
185};
186
187class HARNESS_EXPORT Pipe {
188 public:
189 Pipe(PipeHandle rd, PipeHandle wr) : rd_(std::move(rd)), wr_(std::move(wr)) {}
190
192 SECURITY_ATTRIBUTES *sec_attrs, DWORD sz);
193
194 PipeHandle &read_handle() { return rd_; }
195 PipeHandle &write_handle() { return wr_; }
196
197 private:
198 PipeHandle rd_;
199 PipeHandle wr_;
200};
201
202class ThreadAttributeList {
203 public:
204 ThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST attr_list)
205 : attr_list_(attr_list) {}
206
207 ThreadAttributeList(const ThreadAttributeList &) = delete;
208 ThreadAttributeList &operator=(const ThreadAttributeList &) = delete;
209
210 ThreadAttributeList(ThreadAttributeList &&other)
211 : attr_list_(std::exchange(other.attr_list_, nullptr)) {}
212 ThreadAttributeList &operator=(ThreadAttributeList &other) {
213 attr_list_ = std::exchange(other.attr_list_, nullptr);
214
215 return *this;
216 }
217
218 ~ThreadAttributeList() {
219 if (attr_list_ != nullptr) DeleteProcThreadAttributeList(attr_list_);
220 }
221
223 DWORD count);
224
225 stdx::expected<void, std::error_code> update(DWORD flags, DWORD_PTR attribute,
226 void *value, size_t value_size,
227 void **prev_value,
228 size_t *return_size) const;
229
230 LPPROC_THREAD_ATTRIBUTE_LIST get() const { return attr_list_; }
231
232 private:
233 LPPROC_THREAD_ATTRIBUTE_LIST attr_list_;
234};
235
236} // namespace win32
237#endif
238
239/** an alive, spawned process
240 *
241 * @todo
242 * refactor ProcessLauchner and SpawnedProcess into:
243 * - ProcessLauncher having only the spawn/launch() method and no state
244 * - Process as a thin wrapper around 'pid' and operators on it
245 * - SpawnedProcess being a Process with stdin/stdout/stderr
246 * - a way to declare ownership over the 'pid' (if owned, kill pid in
247 * destructor) For now, this mostly exists to make the move-constructor of
248 * ProcessLauncher easier to implement.
249 */
250class HARNESS_EXPORT SpawnedProcess {
251 public:
252 SpawnedProcess(std::string pexecutable_path, std::vector<std::string> pargs,
253 std::vector<std::pair<std::string, std::string>> penv_vars,
254 bool predirect_stderr = true)
255 : executable_path(std::move(pexecutable_path)),
256 args(std::move(pargs)),
257 env_vars(std::move(penv_vars)),
258 redirect_stderr(predirect_stderr) {}
259
262
265
266 virtual ~SpawnedProcess() = default;
267
268#ifdef _WIN32
269 using handle_type = HANDLE;
270 using id_type = DWORD;
271#else
272 using handle_type = pid_t;
273 using id_type = pid_t;
274#endif
275
276 [[nodiscard]] std::string get_cmd_line() const;
277
278 [[nodiscard]] std::string executable() const { return executable_path; }
279
280 protected:
281 std::string executable_path;
282 std::vector<std::string> args;
283 std::vector<std::pair<std::string, std::string>> env_vars;
284#ifdef _WIN32
285 win32::PipeHandle child_in_wr;
286 win32::PipeHandle child_out_rd;
287 win32::Process process_;
288#else
289 pid_t childpid{-1};
290 int fd_in[2]{-1, -1};
291 int fd_out[2]{-1, -1};
292#endif
294};
295
296// Launches a process as child of current process and exposes the stdin &
297// stdout of the child process (implemented thru pipelines) so the client of
298// this class can read from the child's stdout and write to the child's
299// stdin. For usage, see unit tests.
300//
301class HARNESS_EXPORT ProcessLauncher : public SpawnedProcess {
302 public:
304
305 /**
306 * Creates a new process and launch it.
307 * If redirect_stderr is true, the child's stderr is redirected to the
308 * same stream than child's stdout.
309 */
310 ProcessLauncher(std::string pexecutable_path, std::vector<std::string> pargs,
311 std::vector<std::pair<std::string, std::string>> penv_vars,
312 bool predirect_stderr = true)
313 : SpawnedProcess(std::move(pexecutable_path), std::move(pargs),
314 std::move(penv_vars), predirect_stderr) {}
315
316 // copying a Process results in multiple destructors trying
317 // to kill the same alive process. Disable it.
320
322 : SpawnedProcess(std::move(rhs)),
323 is_alive(std::exchange(rhs.is_alive, false)) {}
324
325 ~ProcessLauncher() override;
326
327 /** Launches the child process, and makes pipes available for read/write.
328 */
329 void start(bool use_std_io_handlers = false);
330
331 void start(bool use_stdout_handler, bool use_stdin_handler);
332
333 /**
334 * Read up to a 'count' bytes from the stdout of the child process.
335 * This method blocks until the amount of bytes is read or specified
336 * timeout expires.
337 * @param buf already allocated buffer where the read data will be stored.
338 * @param count the maximum amount of bytes to read.
339 * @param timeout timeout (in milliseconds) for the read to complete
340 * @return the real number of bytes read.
341 * Returns an shcore::Exception in case of error when reading.
342 */
343 int read(char *buf, size_t count, std::chrono::milliseconds timeout);
344
345 /**
346 * Writes several butes into stdin of child process.
347 * Returns an shcore::Exception in case of error when writing.
348 */
349 int write(const char *buf, size_t count);
350
351 /**
352 * Kills the child process and returns process' exit code.
353 */
354 exit_status_type kill();
355
358
359 /**
360 * Returns the child process id.
361 */
362 process_id_type get_pid() const;
363
364 /**
365 * Returns the child process handle.
366 */
367 process_handle_type get_process_handle() const;
368
369 /**
370 * get exit-code.
371 */
373
374 /**
375 * Wait for the child process to exists and returns its exit code.
376 * If the child process is already dead, wait() just returns.
377 *
378 * @returns the exit code of the process.
379 * @throws std::runtime_error if process exited with a signal
380 */
381
383
384 exit_status_type native_wait(
386
387 /**
388 * Closes pipe to process' STDIN in order to notify the process that all
389 * data was sent.
390 */
391 void end_of_write();
392
393 enum class ShutdownEvent {
394 TERM, // clean shutdown (ie. SIGTERM on Unix)
395 KILL, // immediate (and abrupt) shutdown (ie. SIGKILL on Unix)
396 ABRT // try to generate a stacktrace
397 };
398 /**
399 * Sends a shutdown event to child process (SIGTERM on Unix, Ctrl+C on
400 * Win)
401 *
402 * @param event type of shutdown event
403 * @return std::error_code indicating success/failure
404 */
405 std::error_code send_shutdown_event(
406 ShutdownEvent event = ShutdownEvent::TERM) const noexcept;
407
408 private:
409 /**
410 * Closes child process and returns process' exit code.
411 *
412 * @throws std::system_error if sending signal to child process fails
413 * @throws std::runtime_error if waiting for process to change state fails
414 *
415 * @return process exit code.
416 */
417 exit_status_type close();
418
419 std::mutex fd_in_mtx_;
420 std::mutex fd_out_mtx_;
421
422 bool is_alive{false};
423};
424
425} // end of namespace mysql_harness
426
427#endif // _PROCESS_LAUNCHER_H_
Kerberos Client Authentication nullptr
Definition: auth_kerberos_client_plugin.cc:247
exit status of processes.
Definition: exit_status.h:47
Runs a specified command line and calls a callback for all data that is written by the child program ...
Definition: create_def.cc:125
Definition: process_launcher.h:301
ProcessLauncher(ProcessLauncher &&rhs) noexcept
Definition: process_launcher.h:321
ProcessLauncher(std::string pexecutable_path, std::vector< std::string > pargs, std::vector< std::pair< std::string, std::string > > penv_vars, bool predirect_stderr=true)
Creates a new process and launch it.
Definition: process_launcher.h:310
SpawnedProcess::id_type process_id_type
Definition: process_launcher.h:357
std::mutex fd_out_mtx_
Definition: process_launcher.h:420
ProcessLauncher operator=(const ProcessLauncher &)=delete
std::mutex fd_in_mtx_
Definition: process_launcher.h:419
ShutdownEvent
Definition: process_launcher.h:393
SpawnedProcess::handle_type process_handle_type
Definition: process_launcher.h:356
ProcessLauncher(const ProcessLauncher &)=delete
an alive, spawned process
Definition: process_launcher.h:250
virtual ~SpawnedProcess()=default
bool redirect_stderr
Definition: process_launcher.h:293
pid_t id_type
Definition: process_launcher.h:273
std::string executable_path
Definition: process_launcher.h:281
std::vector< std::pair< std::string, std::string > > env_vars
Definition: process_launcher.h:283
pid_t handle_type
Definition: process_launcher.h:272
SpawnedProcess(const SpawnedProcess &)=delete
SpawnedProcess(std::string pexecutable_path, std::vector< std::string > pargs, std::vector< std::pair< std::string, std::string > > penv_vars, bool predirect_stderr=true)
Definition: process_launcher.h:252
std::string executable() const
Definition: process_launcher.h:278
std::vector< std::string > args
Definition: process_launcher.h:282
SpawnedProcess(SpawnedProcess &&)=default
SpawnedProcess & operator=(const SpawnedProcess &)=delete
SpawnedProcess & operator=(SpawnedProcess &&)=default
Definition: expected.h:286
constexpr DWORD buf_size
Definition: create_def.cc:229
static int flags[50]
Definition: hp_test1.cc:40
static void start(mysql_harness::PluginFuncEnv *env)
Definition: http_auth_backend_plugin.cc:180
static mi_bit_type mask[]
Definition: mi_packrec.cc:141
static my_thread_id thread_id
Definition: my_thr_init.cc:60
static int count
Definition: myisam_ftdump.cc:45
static uint update
Definition: myisamlog.cc:94
Definition: buf0block_hint.cc:30
static bool timeout(bool(*wait_condition)())
Timeout function.
Definition: log0meb.cc:498
std::chrono::milliseconds milliseconds
Definition: authorize_manager.cc:67
ValueType value(const std::optional< ValueType > &v)
Definition: gtid.h:83
static int wait(mysql_cond_t *that, mysql_mutex_t *mutex_arg, const char *, unsigned int)
Definition: mysql_cond_v1_native.cc:62
static mysql_service_status_t get(THD **thd) noexcept
Definition: mysql_current_thread_reader_all_empty.cc:31
Definition: common.h:44
static mysql_service_status_t create(my_h_string *) noexcept
Definition: mysql_string_all_empty.cc:43
stdx::expected< void, std::error_code > close(file_handle_type native_handle)
close file handle.
Definition: file.h:239
stdx::expected< size_t, std::error_code > write(SyncWriteStream &stream, const ConstBufferSequence &buffers)
Definition: buffer.h:977
stdx::expected< size_t, std::error_code > read(SyncReadStream &stream, const MutableBufferSequence &buffers)
Definition: buffer.h:835
Definition: gcs_xcom_synode.h:64
native_handle_type native_handle()
Definition: process.h:56
std::vector< T, ut::allocator< T > > vector
Specialization of vector which uses allocator.
Definition: ut0new.h:2880
required string event
Definition: replication_group_member_actions.proto:32
@ KILL
Definition: task.h:232
#define HANDLE
Definition: violite.h:159