MySQL 8.0.39
Source Code Documentation
win32_named_pipe.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2020, 2024, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is designed to work with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have either included with
14 the program or referenced in the documentation.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24*/
25#ifndef MYSQL_HARNESS_NET_TS_WIN32_NAMED_PIPE_H_
26#define MYSQL_HARNESS_NET_TS_WIN32_NAMED_PIPE_H_
27
29#if defined(_WIN32)
30#include <system_error>
31
32#include <Windows.h>
33
37
38/**
39 * @file
40 *
41 * Named Pipe.
42 *
43 * windows only.
44 *
45 * @note
46 * Experimental. Just barely enough that it mimiks the synchronous interface
47 * for net::basis_socket and allows to:
48 *
49 * - for the server-side: open(), bind(), accept()
50 * - for the client-side: open(), connect(), read(), write()
51 *
52 * and manage the lifetime of the managed HANDLE and automatically close() a
53 * pipe on destruct.
54 *
55 * It doesn't support non-blocking IO, nor overlapped IO.
56 */
57
58namespace win32 {
59inline std::error_code last_error_code() {
60 return {static_cast<int>(GetLastError()), std::system_category()};
61}
62} // namespace win32
63
64namespace local {
65namespace impl {
66namespace named_pipe {
68static const native_handle_type kInvalidHandle{INVALID_HANDLE_VALUE};
69} // namespace named_pipe
70} // namespace impl
71
72/**
73 * implementation base for basic_named_pipe.
74 *
75 * all methods that are independent of the template parameters like Protocol.
76 */
77class basic_named_pipe_impl_base {
78 public:
79 using executor_type = net::io_context::executor_type;
81
82 basic_named_pipe_impl_base(net::io_context &io_ctx) : io_ctx_{&io_ctx} {}
83
84 basic_named_pipe_impl_base(const basic_named_pipe_impl_base &) = delete;
85 basic_named_pipe_impl_base &operator=(const basic_named_pipe_impl_base &) =
86 delete;
87 basic_named_pipe_impl_base(basic_named_pipe_impl_base &&rhs) noexcept
88 : native_handle_{std::exchange(rhs.native_handle_,
90 native_non_blocking_{std::exchange(rhs.native_non_blocking_, 0)},
91 io_ctx_{std::move(rhs.io_ctx_)} {}
92
93 basic_named_pipe_impl_base &operator=(
94 basic_named_pipe_impl_base &&rhs) noexcept {
95 native_handle_ =
96 std::exchange(rhs.native_handle_, impl::named_pipe::kInvalidHandle);
97 native_non_blocking_ = std::exchange(rhs.native_non_blocking_, 0);
98 io_ctx_ = rhs.io_ctx_;
99
100 return *this;
101 }
102
103 ~basic_named_pipe_impl_base() = default;
104
105 constexpr native_handle_type native_handle() const noexcept {
106 return native_handle_;
107 }
108
110
111 bool is_open() const noexcept {
112 return native_handle_ != impl::named_pipe::kInvalidHandle;
113 }
114
115 executor_type get_executor() noexcept { return io_ctx_->get_executor(); }
116
118 // TODO: once the io-context has support for OVERLAPPED IO on windows
119 // a full implemenantation can be done.
120
122 make_error_code(std::errc::function_not_supported));
123 }
124
126 if (is_open()) {
127 cancel();
128 }
129 return std::exchange(native_handle_, impl::named_pipe::kInvalidHandle);
130 }
131
132 protected:
134
135 int native_non_blocking_{0};
136
137 net::io_context *io_ctx_;
138
139 void native_handle(native_handle_type handle) { native_handle_ = handle; }
140};
141
142/**
143 * implementation base class of basic_named_pipe.
144 *
145 * generic functionality that involves the Protocol template parameter like
146 *
147 * - open(), close(), assign(), release()
148 */
149template <class Protocol>
150class basic_named_pipe_impl : public basic_named_pipe_impl_base {
151 private:
152 using __base = basic_named_pipe_impl_base;
153
154 public:
155 using protocol_type = Protocol;
156 using endpoint_type = typename protocol_type::endpoint;
157
158 constexpr explicit basic_named_pipe_impl(net::io_context &ctx) noexcept
159 : __base{ctx} {}
160
161 basic_named_pipe_impl(const basic_named_pipe_impl &) = delete;
162 basic_named_pipe_impl &operator=(const basic_named_pipe_impl &) = delete;
163 basic_named_pipe_impl(basic_named_pipe_impl &&rhs) = default;
164
165 basic_named_pipe_impl &operator=(basic_named_pipe_impl &&rhs) noexcept {
166 if (this == std::addressof(rhs)) {
167 return *this;
168 }
169 __base::operator=(std::move(rhs));
170
171 return *this;
172 }
173
174 ~basic_named_pipe_impl() {}
175
176 executor_type get_executor() noexcept { return __base::get_executor(); }
177
179
180 constexpr bool is_open() const noexcept { return __base::is_open(); }
181
183 const protocol_type &protocol, const native_handle_type &native_handle) {
184 if (is_open()) {
187 }
188 protocol_ = protocol;
189 native_handle_ = native_handle;
190
191 return {};
192 }
193
195 DWORD wait_mode = v ? PIPE_NOWAIT : PIPE_WAIT;
196
197 bool success =
198 SetNamedPipeHandleState(native_handle_, &wait_mode, nullptr, nullptr);
199
200 if (!success) {
202 }
203
204 return {};
205 }
206
207 stdx::expected<void, std::error_code> connect(const endpoint_type &ep) {
208 if (is_open()) {
211 }
212
213 using clock_type = std::chrono::steady_clock;
214 using namespace std::chrono_literals;
215 const auto retry_step = 10ms;
216 const auto end_time = clock_type::now() + 1s;
217 do {
218 native_handle_ =
219 CreateFile(TEXT(ep.path().c_str()), GENERIC_READ | GENERIC_WRITE, 0,
220 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
221
222 if (native_handle_ == impl::named_pipe::kInvalidHandle) {
223 auto const error_code = win32::last_error_code();
224 // if the pipe is not ready, try waiting max 1 second for it to become
225 // available
226 const std::error_code ec_pipe_busy{ERROR_PIPE_BUSY,
227 std::system_category()};
228
229 if ((error_code == ec_pipe_busy) && clock_type::now() < end_time) {
230 std::this_thread::sleep_for(retry_step);
231 continue;
232 }
233
234 return stdx::make_unexpected(error_code);
235 } else
236 break;
237 } while (true);
238
239 return {};
240 }
241
242 private:
243 protocol_type protocol_{endpoint_type{}.protocol()};
244};
245
246/**
247 * base-class of basic_named_pipe_socket.
248 *
249 * provides functionality that's common between socket and acceptor like:
250 *
251 * - read_some
252 * - write_some
253 */
254template <class Protocol>
255class basic_named_pipe : private basic_named_pipe_impl<Protocol> {
256 private:
257 using __base = basic_named_pipe_impl<Protocol>;
258
259 public:
260 using protocol_type = Protocol;
261 using executor_type = typename __base::executor_type;
263 using endpoint_type = typename protocol_type::endpoint;
264
265 // constructors are protected, below.
266
267 executor_type get_executor() noexcept { return __base::get_executor(); }
268
270 const protocol_type &protocol, const native_handle_type &native_handle) {
271 return __base::assign(protocol, native_handle);
272 }
273
275
276 constexpr bool is_open() const noexcept { return __base::is_open(); }
277
281 }
282
285 }
286
287 stdx::expected<void, std::error_code> connect(const endpoint_type &ep) {
288 return __base::connect(ep);
289 }
290
291 template <class MutableBufferSequence>
293 const MutableBufferSequence &buffers) {
294 size_t transferred{};
295 const auto bufend = buffer_sequence_end(buffers);
296 for (auto bufcur = buffer_sequence_begin(buffers); bufcur != bufend;
297 ++bufcur) {
298 DWORD numRead{0};
299 if (!ReadFile(native_handle(), bufcur->data(), bufcur->size(), &numRead,
300 NULL)) {
302 }
303
304 transferred += numRead;
305
306 // for now, only process the first buffer as it simplifies the
307 // error-handling after ReadFile().
308 break;
309 }
310
311 return transferred;
312 }
313
314 template <class ConstBufferSequence>
316 const ConstBufferSequence &buffers) {
317 size_t transferred{};
318
319 const auto bufend = buffer_sequence_end(buffers);
320 for (auto bufcur = buffer_sequence_begin(buffers); bufcur != bufend;
321 ++bufcur) {
322 DWORD written{0};
323
324 if (!WriteFile(native_handle(), bufcur->data(), bufcur->size(), &written,
325 NULL)) {
327 }
328
329 transferred += written;
330
331 // for now, only process the first buffer.
332 break;
333 }
334
335 return transferred;
336 }
337
338 protected:
339 basic_named_pipe(net::io_context &ctx) : __base{ctx} {}
340
341 basic_named_pipe(net::io_context &ctx, const protocol_type &proto)
342 : __base{ctx, proto} {}
343
344 basic_named_pipe(net::io_context &ctx, const protocol_type &proto,
346 : __base{ctx} {
347 assign(proto, native_handle);
348 }
349
350 basic_named_pipe(const basic_named_pipe &) = delete;
351 basic_named_pipe &operator=(const basic_named_pipe &) = delete;
352 basic_named_pipe(basic_named_pipe &&) = default;
353 basic_named_pipe &operator=(basic_named_pipe &&) = default;
354
355 ~basic_named_pipe() = default;
356};
357
358/**
359 * client side of a basic_named_pipe.
360 *
361 * @tparam Protocol a protocol class that provides .protocol() and .type()
362 */
363template <class Protocol>
364class basic_named_pipe_socket : public basic_named_pipe<Protocol> {
365 private:
366 using __base = basic_named_pipe<Protocol>;
367
368 public:
369 using protocol_type = Protocol;
371 using endpoint_type = typename protocol_type::endpoint;
372
373 explicit basic_named_pipe_socket(net::io_context &ctx) : __base(ctx) {}
374 basic_named_pipe_socket(net::io_context &ctx, const protocol_type &proto)
375 : __base(ctx, proto) {}
376
377 basic_named_pipe_socket(net::io_context &ctx, const protocol_type &proto,
379 : __base(ctx, proto, native_handle) {}
380
381 basic_named_pipe_socket(const basic_named_pipe_socket &) = delete;
382 basic_named_pipe_socket &operator=(const basic_named_pipe_socket &) = delete;
383
384 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
385 basic_named_pipe_socket(basic_named_pipe_socket &&other) = default;
386 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
387 basic_named_pipe_socket &operator=(basic_named_pipe_socket &&) = default;
388
389 ~basic_named_pipe_socket() {
390 if (is_open()) close();
391 }
392
393 constexpr bool is_open() const noexcept { return __base::is_open(); }
394
396
398 if (is_open()) {
401 }
402
403 return {};
404 }
405
407 if (is_open()) {
408 DisconnectNamedPipe(native_handle());
410 }
411
412 return {};
413 }
414
415 template <class ConstBufferSequence>
417 const ConstBufferSequence &buffers) {
418 return __base::write_some(buffers);
419 }
420
422 if (is_open()) {
424 } else {
425 native_non_blocking_ = static_cast<int>(v);
426
427 return {};
428 }
429 }
430
431 stdx::expected<void, std::error_code> connect(const endpoint_type &ep) {
432 auto connect_res = __base::connect(ep);
433 if (!connect_res) return connect_res;
434
435 if (native_non_blocking_ == 1) {
437 }
438
439 return connect_res;
440 }
441
442 private:
443 int native_non_blocking_{-1};
444};
445
446/**
447 * server side of a named pipe.
448 *
449 * can accept a connection from an named pipe path.
450 */
451template <class Protocol>
452class basic_named_pipe_acceptor : public basic_named_pipe_impl<Protocol> {
453 private:
454 using __base = basic_named_pipe_impl<Protocol>;
455
456 public:
457 using protocol_type = Protocol;
458 using socket_type = basic_named_pipe_socket<protocol_type>;
459 using endpoint_type = typename protocol_type::endpoint;
460 using executor_type = typename __base::executor_type;
462
463 explicit basic_named_pipe_acceptor(net::io_context &ctx) : __base(ctx) {}
464 basic_named_pipe_acceptor(net::io_context &ctx, const protocol_type &proto)
465 : __base(ctx, proto) {}
466
467 basic_named_pipe_acceptor(const basic_named_pipe_acceptor &) = delete;
468 basic_named_pipe_acceptor &operator=(const basic_named_pipe_acceptor &) =
469 delete;
470
471 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
472 basic_named_pipe_acceptor(basic_named_pipe_acceptor &&other) = default;
473 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
474 basic_named_pipe_acceptor &operator=(basic_named_pipe_acceptor &&) = default;
475
476 ~basic_named_pipe_acceptor() {
477 if (is_open()) close();
478 }
479
480 executor_type get_executor() noexcept { return __base::get_executor(); }
481
482 bool is_open() const { return __base::is_open(); }
483
485
488 }
489
491
493 if (is_open()) {
494 CloseHandle(native_handle());
496 }
497
498 return {};
499 }
500
502 return __base::release();
503 }
504
506 if (is_open()) {
508 } else {
509 native_non_blocking_ = static_cast<int>(v);
510
511 return {};
512 }
513 }
514
515 /**
516 * bind endpoint to socket.
517 *
518 * in accordance with net-ts' acceptors' bind().
519 *
520 * @param ep an endpoint this method binds to a socket
521 * @param flags flags passed to CreateNamedPipe() as is. Use for PIPE_NOWAIT.
522 *
523 * @retval std::errc::invalid_argument if ep.path() is empty
524 * @retval std::errc::invalid_argument if already bound.
525 */
526 stdx::expected<void, std::error_code> bind(const endpoint_type &ep,
527 int flags = 0) {
528 if (ep.path().empty()) {
530 make_error_code(std::errc::invalid_argument));
531 }
532
533 // already bound.
534 if (!ep_.path().empty()) {
536 make_error_code(std::errc::invalid_argument));
537 }
538
539 ep_ = ep;
540
541 const auto protocol = protocol_type();
542
543 if (native_non_blocking_ == 1) flags |= PIPE_NOWAIT;
544
545 if (!is_open()) {
546 auto handle = CreateNamedPipe(
547 TEXT(ep_.path().c_str()), PIPE_ACCESS_DUPLEX,
548 protocol.type() | protocol.protocol() | PIPE_REJECT_REMOTE_CLIENTS |
549 flags,
550 back_log_, 1024 * 16, 1024 * 16, NMPWAIT_USE_DEFAULT_WAIT, NULL);
551
553 auto ec = win32::last_error_code();
554 return stdx::make_unexpected(ec);
555 }
556
558 }
559
560 return {};
561 }
562
563 /**
564 * max waiting pending connections.
565 */
567 int back_log = PIPE_UNLIMITED_INSTANCES) {
568 if (back_log <= 0) {
570 make_error_code(std::errc::invalid_argument));
571 }
572 if (back_log > PIPE_UNLIMITED_INSTANCES) {
574 make_error_code(std::errc::invalid_argument));
575 }
576
577 back_log_ = back_log;
578
579 return {};
580 }
581
582 /**
583 * accept a client connection.
584 *
585 * - CreateNamedPipe() on the endpoint assigned by bind()
586 * - ConnectNamedPipe() to accept the client connection.
587 *
588 * @return a connected named pipe.
589 * @retval std::errc::invalid_argument if no endpoint bound.
590 */
592 // not bound.
593 if (ep_.path().empty()) {
595 make_error_code(std::errc::invalid_argument));
596 }
597
598 const auto protocol = protocol_type();
599
600 const bool connected = ConnectNamedPipe(native_handle(), NULL);
601 if (!connected) {
602 auto last_ec = win32::last_error_code();
603
604 const std::error_code ec_pipe_connected{ERROR_PIPE_CONNECTED, // 535
605 std::system_category()};
606 const std::error_code ec_no_data{ERROR_NO_DATA, // 232
607 std::system_category()};
608
609 // ERROR_PIPE_CONNECTED is also a success
610 // ERROR_NO_DATA too, it just means the pipe is already closed, but quite
611 // likely readable.
612 if (last_ec == ec_pipe_connected || last_ec == ec_no_data) {
613 return {std::in_place, get_executor().context(), protocol,
614 native_handle()};
615 }
616
617 return stdx::make_unexpected(last_ec);
618 }
619
620 return {std::in_place, get_executor().context(), protocol, native_handle()};
621 }
622
624 return ep_;
625 }
626
627 private:
628 endpoint_type ep_;
629 int back_log_{PIPE_UNLIMITED_INSTANCES};
630 int native_non_blocking_{-1}; // unset.
631};
632
633/**
634 * endpoint of a named pipe.
635 */
636template <typename Protocol>
637class basic_named_pipe_endpoint {
638 public:
639 using protocol_type = Protocol;
640
641 basic_named_pipe_endpoint() = default;
642 basic_named_pipe_endpoint(std::string path)
643 : path_{path.substr(0, std::min(max_path_len(), path.size()))} {}
644
645 std::string path() const { return path_; }
646
647 constexpr protocol_type protocol() const noexcept { return protocol_type(); }
648
649 size_t size() const { return path().size(); }
650
651 size_t capacity() const { return max_path_len(); }
652
653 void resize(size_t size) { path_.resize(size); }
654
655 private:
656 constexpr size_t max_path_len() const { return 256; }
657
658 std::string path_;
659};
660
661/**
662 * protocol class for message oriented named pipes.
663 */
664class message_protocol {
665 public:
666 using endpoint = basic_named_pipe_endpoint<message_protocol>;
667 using socket = basic_named_pipe_socket<message_protocol>;
668 using acceptor = basic_named_pipe_acceptor<message_protocol>;
669
670 int type() const { return PIPE_TYPE_MESSAGE; }
671 int protocol() const { return PIPE_READMODE_MESSAGE; }
672};
673
674/**
675 * protocol class for byte oriented named pipes.
676 */
677class byte_protocol {
678 public:
679 using endpoint = basic_named_pipe_endpoint<byte_protocol>;
680 using socket = basic_named_pipe_socket<byte_protocol>;
681 using acceptor = basic_named_pipe_acceptor<byte_protocol>;
682
683 int type() const { return PIPE_TYPE_BYTE; }
684 int protocol() const { return PIPE_READMODE_BYTE; }
685};
686
687} // namespace local
688
689#endif
690
691#endif
Definition: protocol.h:33
Definition: io_context.h:990
Definition: io_context.h:61
Definition: expected.h:944
static int flags[50]
Definition: hp_test1.cc:40
static bool connected
Definition: mysql.cc:156
ulong back_log
Definition: mysqld.cc:1329
static char * path
Definition: mysqldump.cc:137
Definition: authentication.cc:36
Definition: local.h:60
std::error_code make_error_code(DynamicLoaderErrc ec)
make error_code from a DynamicLoaderErrc.
Definition: dynamic_loader.cc:79
@ success
A grow operation succeeded.
constexpr file_handle_type kInvalidHandle
Definition: file.h:54
std::error_code last_error_code()
Definition: file.h:57
stdx::expected< void, std::error_code > close(file_handle_type native_handle)
close file handle.
Definition: file.h:239
stdx::expected< void, error_type > listen(native_handle_type native_handle, int backlog)
Definition: socket.h:149
stdx::expected< native_handle_type, error_type > socket(int family, int sock_type, int protocol)
Definition: socket.h:63
stdx::expected< void, error_type > bind(native_handle_type native_handle, const struct sockaddr *addr, size_t addr_len)
wrap bind() in a portable way.
Definition: socket.h:339
stdx::expected< native_handle_type, error_type > accept(native_handle_type native_handle, struct sockaddr *addr, socklen_t *addr_len)
wrap accept() in a portable way.
Definition: socket.h:367
stdx::expected< bool, error_type > native_non_blocking(native_handle_type native_handle)
Definition: socket.h:106
stdx::expected< void, error_type > connect(native_handle_type native_handle, const struct sockaddr *addr, size_t addr_len)
wrap connect() in a portable way.
Definition: socket.h:353
int native_handle_type
Definition: socket_constants.h:51
const const_buffer * buffer_sequence_end(const const_buffer &b) noexcept
Definition: buffer.h:185
const const_buffer * buffer_sequence_begin(const const_buffer &b) noexcept
Definition: buffer.h:180
static int handle(int sql_errno, const char *sqlstate, const char *message, void *state)
Bridge function between the C++ API offered by this module and the C API of the parser service.
Definition: services.cc:64
Definition: gcs_xcom_synode.h:64
stdx::expected< int, std::error_code > open(const char *fname, int flags, mode_t mode) noexcept
Definition: file_handle.cc:79
native_handle_type native_handle()
Definition: process.h:56
constexpr auto make_unexpected(E &&e) -> unexpected< std::decay_t< E > >
Definition: expected.h:125
required string type
Definition: replication_group_member_actions.proto:34
#define NULL
Definition: types.h:55
#define HANDLE
Definition: violite.h:159