MySQL 8.3.0
Source Code Documentation
win32_named_pipe.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2020, 2023, 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 also distributed 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 included with MySQL.
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 MYSQL_HARNESS_NET_TS_WIN32_NAMED_PIPE_H_
25#define MYSQL_HARNESS_NET_TS_WIN32_NAMED_PIPE_H_
26
28#if defined(_WIN32)
29#include <system_error>
30
31#include <Windows.h>
32
36
37/**
38 * @file
39 *
40 * Named Pipe.
41 *
42 * windows only.
43 *
44 * @note
45 * Experimental. Just barely enough that it mimiks the synchronous interface
46 * for net::basis_socket and allows to:
47 *
48 * - for the server-side: open(), bind(), accept()
49 * - for the client-side: open(), connect(), read(), write()
50 *
51 * and manage the lifetime of the managed HANDLE and automatically close() a
52 * pipe on destruct.
53 *
54 * It doesn't support non-blocking IO, nor overlapped IO.
55 */
56
57namespace win32 {
58inline std::error_code last_error_code() {
59 return {static_cast<int>(GetLastError()), std::system_category()};
60}
61} // namespace win32
62
63namespace local {
64namespace impl {
65namespace named_pipe {
67static const native_handle_type kInvalidHandle{INVALID_HANDLE_VALUE};
68} // namespace named_pipe
69} // namespace impl
70
71/**
72 * implementation base for basic_named_pipe.
73 *
74 * all methods that are independent of the template parameters like Protocol.
75 */
76class basic_named_pipe_impl_base {
77 public:
78 using executor_type = net::io_context::executor_type;
80
81 basic_named_pipe_impl_base(net::io_context &io_ctx) : io_ctx_{&io_ctx} {}
82
83 basic_named_pipe_impl_base(const basic_named_pipe_impl_base &) = delete;
84 basic_named_pipe_impl_base &operator=(const basic_named_pipe_impl_base &) =
85 delete;
86 basic_named_pipe_impl_base(basic_named_pipe_impl_base &&rhs) noexcept
87 : native_handle_{std::exchange(rhs.native_handle_,
89 native_non_blocking_{std::exchange(rhs.native_non_blocking_, 0)},
90 io_ctx_{std::move(rhs.io_ctx_)} {}
91
92 basic_named_pipe_impl_base &operator=(
93 basic_named_pipe_impl_base &&rhs) noexcept {
94 native_handle_ =
95 std::exchange(rhs.native_handle_, impl::named_pipe::kInvalidHandle);
96 native_non_blocking_ = std::exchange(rhs.native_non_blocking_, 0);
97 io_ctx_ = rhs.io_ctx_;
98
99 return *this;
100 }
101
102 ~basic_named_pipe_impl_base() = default;
103
104 constexpr native_handle_type native_handle() const noexcept {
105 return native_handle_;
106 }
107
109
110 bool is_open() const noexcept {
111 return native_handle_ != impl::named_pipe::kInvalidHandle;
112 }
113
114 executor_type get_executor() noexcept { return io_ctx_->get_executor(); }
115
117 // TODO: once the io-context has support for OVERLAPPED IO on windows
118 // a full implemenantation can be done.
119
121 make_error_code(std::errc::function_not_supported));
122 }
123
125 if (is_open()) {
126 cancel();
127 }
128 return std::exchange(native_handle_, impl::named_pipe::kInvalidHandle);
129 }
130
131 protected:
133
134 int native_non_blocking_{0};
135
136 net::io_context *io_ctx_;
137
138 void native_handle(native_handle_type handle) { native_handle_ = handle; }
139};
140
141/**
142 * implementation base class of basic_named_pipe.
143 *
144 * generic functionality that involves the Protocol template parameter like
145 *
146 * - open(), close(), assign(), release()
147 */
148template <class Protocol>
149class basic_named_pipe_impl : public basic_named_pipe_impl_base {
150 private:
151 using __base = basic_named_pipe_impl_base;
152
153 public:
154 using protocol_type = Protocol;
155 using endpoint_type = typename protocol_type::endpoint;
156
157 constexpr explicit basic_named_pipe_impl(net::io_context &ctx) noexcept
158 : __base{ctx} {}
159
160 basic_named_pipe_impl(const basic_named_pipe_impl &) = delete;
161 basic_named_pipe_impl &operator=(const basic_named_pipe_impl &) = delete;
162 basic_named_pipe_impl(basic_named_pipe_impl &&rhs) = default;
163
164 basic_named_pipe_impl &operator=(basic_named_pipe_impl &&rhs) noexcept {
165 if (this == std::addressof(rhs)) {
166 return *this;
167 }
168 __base::operator=(std::move(rhs));
169
170 return *this;
171 }
172
173 ~basic_named_pipe_impl() {}
174
175 executor_type get_executor() noexcept { return __base::get_executor(); }
176
178
179 constexpr bool is_open() const noexcept { return __base::is_open(); }
180
182 const protocol_type &protocol, const native_handle_type &native_handle) {
183 if (is_open()) {
186 }
187 protocol_ = protocol;
188 native_handle_ = native_handle;
189
190 return {};
191 }
192
194 DWORD wait_mode = v ? PIPE_NOWAIT : PIPE_WAIT;
195
196 bool success =
197 SetNamedPipeHandleState(native_handle_, &wait_mode, nullptr, nullptr);
198
199 if (!success) {
201 }
202
203 return {};
204 }
205
206 stdx::expected<void, std::error_code> connect(const endpoint_type &ep) {
207 if (is_open()) {
210 }
211
212 using clock_type = std::chrono::steady_clock;
213 using namespace std::chrono_literals;
214 const auto retry_step = 10ms;
215 const auto end_time = clock_type::now() + 1s;
216 do {
217 native_handle_ =
218 CreateFile(TEXT(ep.path().c_str()), GENERIC_READ | GENERIC_WRITE, 0,
219 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
220
221 if (native_handle_ == impl::named_pipe::kInvalidHandle) {
222 auto const error_code = win32::last_error_code();
223 // if the pipe is not ready, try waiting max 1 second for it to become
224 // available
225 const std::error_code ec_pipe_busy{ERROR_PIPE_BUSY,
226 std::system_category()};
227
228 if ((error_code == ec_pipe_busy) && clock_type::now() < end_time) {
229 std::this_thread::sleep_for(retry_step);
230 continue;
231 }
232
233 return stdx::make_unexpected(error_code);
234 } else
235 break;
236 } while (true);
237
238 return {};
239 }
240
241 private:
242 protocol_type protocol_{endpoint_type{}.protocol()};
243};
244
245/**
246 * base-class of basic_named_pipe_socket.
247 *
248 * provides functionality that's common between socket and acceptor like:
249 *
250 * - read_some
251 * - write_some
252 */
253template <class Protocol>
254class basic_named_pipe : private basic_named_pipe_impl<Protocol> {
255 private:
256 using __base = basic_named_pipe_impl<Protocol>;
257
258 public:
259 using protocol_type = Protocol;
260 using executor_type = typename __base::executor_type;
262 using endpoint_type = typename protocol_type::endpoint;
263
264 // constructors are protected, below.
265
266 executor_type get_executor() noexcept { return __base::get_executor(); }
267
269 const protocol_type &protocol, const native_handle_type &native_handle) {
270 return __base::assign(protocol, native_handle);
271 }
272
274
275 constexpr bool is_open() const noexcept { return __base::is_open(); }
276
280 }
281
284 }
285
286 stdx::expected<void, std::error_code> connect(const endpoint_type &ep) {
287 return __base::connect(ep);
288 }
289
290 template <class MutableBufferSequence>
292 const MutableBufferSequence &buffers) {
293 size_t transferred{};
294 const auto bufend = buffer_sequence_end(buffers);
295 for (auto bufcur = buffer_sequence_begin(buffers); bufcur != bufend;
296 ++bufcur) {
297 DWORD numRead{0};
298 if (!ReadFile(native_handle(), bufcur->data(), bufcur->size(), &numRead,
299 nullptr)) {
301 }
302
303 transferred += numRead;
304
305 // for now, only process the first buffer as it simplifies the
306 // error-handling after ReadFile().
307 break;
308 }
309
310 return transferred;
311 }
312
313 template <class ConstBufferSequence>
315 const ConstBufferSequence &buffers) {
316 size_t transferred{};
317
318 const auto bufend = buffer_sequence_end(buffers);
319 for (auto bufcur = buffer_sequence_begin(buffers); bufcur != bufend;
320 ++bufcur) {
321 DWORD written{0};
322
323 if (!WriteFile(native_handle(), bufcur->data(), bufcur->size(), &written,
324 nullptr)) {
326 }
327
328 transferred += written;
329
330 // for now, only process the first buffer.
331 break;
332 }
333
334 return transferred;
335 }
336
337 protected:
338 basic_named_pipe(net::io_context &ctx) : __base{ctx} {}
339
340 basic_named_pipe(net::io_context &ctx, const protocol_type &proto)
341 : __base{ctx, proto} {}
342
343 basic_named_pipe(net::io_context &ctx, const protocol_type &proto,
345 : __base{ctx} {
346 assign(proto, native_handle);
347 }
348
349 basic_named_pipe(const basic_named_pipe &) = delete;
350 basic_named_pipe &operator=(const basic_named_pipe &) = delete;
351 basic_named_pipe(basic_named_pipe &&) = default;
352 basic_named_pipe &operator=(basic_named_pipe &&) = default;
353
354 ~basic_named_pipe() = default;
355};
356
357/**
358 * client side of a basic_named_pipe.
359 *
360 * @tparam Protocol a protocol class that provides .protocol() and .type()
361 */
362template <class Protocol>
363class basic_named_pipe_socket : public basic_named_pipe<Protocol> {
364 private:
365 using __base = basic_named_pipe<Protocol>;
366
367 public:
368 using protocol_type = Protocol;
370 using endpoint_type = typename protocol_type::endpoint;
371
372 explicit basic_named_pipe_socket(net::io_context &ctx) : __base(ctx) {}
373 basic_named_pipe_socket(net::io_context &ctx, const protocol_type &proto)
374 : __base(ctx, proto) {}
375
376 basic_named_pipe_socket(net::io_context &ctx, const protocol_type &proto,
378 : __base(ctx, proto, native_handle) {}
379
380 basic_named_pipe_socket(const basic_named_pipe_socket &) = delete;
381 basic_named_pipe_socket &operator=(const basic_named_pipe_socket &) = delete;
382
383 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
384 basic_named_pipe_socket(basic_named_pipe_socket &&other) = default;
385 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
386 basic_named_pipe_socket &operator=(basic_named_pipe_socket &&) = default;
387
388 ~basic_named_pipe_socket() {
389 if (is_open()) close();
390 }
391
392 constexpr bool is_open() const noexcept { return __base::is_open(); }
393
395
397 if (is_open()) {
400 }
401
402 return {};
403 }
404
406 if (is_open()) {
407 DisconnectNamedPipe(native_handle());
409 }
410
411 return {};
412 }
413
414 template <class ConstBufferSequence>
416 const ConstBufferSequence &buffers) {
417 return __base::write_some(buffers);
418 }
419
421 if (is_open()) {
423 } else {
424 native_non_blocking_ = static_cast<int>(v);
425
426 return {};
427 }
428 }
429
430 stdx::expected<void, std::error_code> connect(const endpoint_type &ep) {
431 auto connect_res = __base::connect(ep);
432 if (!connect_res) return connect_res;
433
434 if (native_non_blocking_ == 1) {
436 }
437
438 return connect_res;
439 }
440
441 private:
442 int native_non_blocking_{-1};
443};
444
445/**
446 * server side of a named pipe.
447 *
448 * can accept a connection from an named pipe path.
449 */
450template <class Protocol>
451class basic_named_pipe_acceptor : public basic_named_pipe_impl<Protocol> {
452 private:
453 using __base = basic_named_pipe_impl<Protocol>;
454
455 public:
456 using protocol_type = Protocol;
457 using socket_type = basic_named_pipe_socket<protocol_type>;
458 using endpoint_type = typename protocol_type::endpoint;
459 using executor_type = typename __base::executor_type;
461
462 explicit basic_named_pipe_acceptor(net::io_context &ctx) : __base(ctx) {}
463 basic_named_pipe_acceptor(net::io_context &ctx, const protocol_type &proto)
464 : __base(ctx, proto) {}
465
466 basic_named_pipe_acceptor(const basic_named_pipe_acceptor &) = delete;
467 basic_named_pipe_acceptor &operator=(const basic_named_pipe_acceptor &) =
468 delete;
469
470 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
471 basic_named_pipe_acceptor(basic_named_pipe_acceptor &&other) = default;
472 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
473 basic_named_pipe_acceptor &operator=(basic_named_pipe_acceptor &&) = default;
474
475 ~basic_named_pipe_acceptor() {
476 if (is_open()) close();
477 }
478
479 executor_type get_executor() noexcept { return __base::get_executor(); }
480
481 bool is_open() const { return __base::is_open(); }
482
484
487 }
488
490
492 if (is_open()) {
493 CloseHandle(native_handle());
495 }
496
497 return {};
498 }
499
501 return __base::release();
502 }
503
505 if (is_open()) {
507 } else {
508 native_non_blocking_ = static_cast<int>(v);
509
510 return {};
511 }
512 }
513
514 /**
515 * bind endpoint to socket.
516 *
517 * in accordance with net-ts' acceptors' bind().
518 *
519 * @param ep an endpoint this method binds to a socket
520 * @param flags flags passed to CreateNamedPipe() as is. Use for PIPE_NOWAIT.
521 *
522 * @retval std::errc::invalid_argument if ep.path() is empty
523 * @retval std::errc::invalid_argument if already bound.
524 */
525 stdx::expected<void, std::error_code> bind(const endpoint_type &ep,
526 int flags = 0) {
527 if (ep.path().empty()) {
529 make_error_code(std::errc::invalid_argument));
530 }
531
532 // already bound.
533 if (!ep_.path().empty()) {
535 make_error_code(std::errc::invalid_argument));
536 }
537
538 ep_ = ep;
539
540 const auto protocol = protocol_type();
541
542 if (native_non_blocking_ == 1) flags |= PIPE_NOWAIT;
543
544 if (!is_open()) {
545 auto handle = CreateNamedPipe(
546 TEXT(ep_.path().c_str()), PIPE_ACCESS_DUPLEX,
547 protocol.type() | protocol.protocol() | PIPE_REJECT_REMOTE_CLIENTS |
548 flags,
549 back_log_, 1024 * 16, 1024 * 16, NMPWAIT_USE_DEFAULT_WAIT, nullptr);
550
552 auto ec = win32::last_error_code();
553 return stdx::make_unexpected(ec);
554 }
555
557 }
558
559 return {};
560 }
561
562 /**
563 * max waiting pending connections.
564 */
566 int back_log = PIPE_UNLIMITED_INSTANCES) {
567 if (back_log <= 0) {
569 make_error_code(std::errc::invalid_argument));
570 }
571 if (back_log > PIPE_UNLIMITED_INSTANCES) {
573 make_error_code(std::errc::invalid_argument));
574 }
575
576 back_log_ = back_log;
577
578 return {};
579 }
580
581 /**
582 * accept a client connection.
583 *
584 * - CreateNamedPipe() on the endpoint assigned by bind()
585 * - ConnectNamedPipe() to accept the client connection.
586 *
587 * @return a connected named pipe.
588 * @retval std::errc::invalid_argument if no endpoint bound.
589 */
591 // not bound.
592 if (ep_.path().empty()) {
594 make_error_code(std::errc::invalid_argument));
595 }
596
597 const auto protocol = protocol_type();
598
599 const bool connected = ConnectNamedPipe(native_handle(), nullptr);
600 if (!connected) {
601 auto last_ec = win32::last_error_code();
602
603 const std::error_code ec_pipe_connected{ERROR_PIPE_CONNECTED, // 535
604 std::system_category()};
605 const std::error_code ec_no_data{ERROR_NO_DATA, // 232
606 std::system_category()};
607
608 // ERROR_PIPE_CONNECTED is also a success
609 // ERROR_NO_DATA too, it just means the pipe is already closed, but quite
610 // likely readable.
611 if (last_ec == ec_pipe_connected || last_ec == ec_no_data) {
612 return {std::in_place, get_executor().context(), protocol,
613 native_handle()};
614 }
615
616 return stdx::make_unexpected(last_ec);
617 }
618
619 return {std::in_place, get_executor().context(), protocol, native_handle()};
620 }
621
623 return ep_;
624 }
625
626 private:
627 endpoint_type ep_;
628 int back_log_{PIPE_UNLIMITED_INSTANCES};
629 int native_non_blocking_{-1}; // unset.
630};
631
632/**
633 * endpoint of a named pipe.
634 */
635template <typename Protocol>
636class basic_named_pipe_endpoint {
637 public:
638 using protocol_type = Protocol;
639
640 basic_named_pipe_endpoint() = default;
641 basic_named_pipe_endpoint(std::string path)
642 : path_{path.substr(0, std::min(max_path_len(), path.size()))} {}
643
644 std::string path() const { return path_; }
645
646 constexpr protocol_type protocol() const noexcept { return protocol_type(); }
647
648 size_t size() const { return path().size(); }
649
650 size_t capacity() const { return max_path_len(); }
651
652 void resize(size_t size) { path_.resize(size); }
653
654 private:
655 constexpr size_t max_path_len() const { return 256; }
656
657 std::string path_;
658};
659
660/**
661 * protocol class for message oriented named pipes.
662 */
663class message_protocol {
664 public:
665 using endpoint = basic_named_pipe_endpoint<message_protocol>;
666 using socket = basic_named_pipe_socket<message_protocol>;
667 using acceptor = basic_named_pipe_acceptor<message_protocol>;
668
669 int type() const { return PIPE_TYPE_MESSAGE; }
670 int protocol() const { return PIPE_READMODE_MESSAGE; }
671};
672
673/**
674 * protocol class for byte oriented named pipes.
675 */
676class byte_protocol {
677 public:
678 using endpoint = basic_named_pipe_endpoint<byte_protocol>;
679 using socket = basic_named_pipe_socket<byte_protocol>;
680 using acceptor = basic_named_pipe_acceptor<byte_protocol>;
681
682 int type() const { return PIPE_TYPE_BYTE; }
683 int protocol() const { return PIPE_READMODE_BYTE; }
684};
685
686} // namespace local
687
688#endif
689
690#endif
Definition: protocol.h:32
Definition: io_context.h:989
Definition: io_context.h:60
Definition: expected.h:943
static int flags[50]
Definition: hp_test1.cc:39
static bool connected
Definition: mysql.cc:168
ulong back_log
Definition: mysqld.cc:1342
static char * path
Definition: mysqldump.cc:148
Definition: authentication.cc:35
Definition: local.h:59
std::error_code make_error_code(DynamicLoaderErrc ec)
make error_code from a DynamicLoaderErrc.
Definition: dynamic_loader.cc:78
constexpr file_handle_type kInvalidHandle
Definition: file.h:53
std::error_code last_error_code()
Definition: file.h:56
stdx::expected< void, std::error_code > close(file_handle_type native_handle)
close file handle.
Definition: file.h:238
stdx::expected< void, error_type > listen(native_handle_type native_handle, int backlog)
Definition: socket.h:148
stdx::expected< native_handle_type, error_type > socket(int family, int sock_type, int protocol)
Definition: socket.h:62
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:338
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:366
stdx::expected< bool, error_type > native_non_blocking(native_handle_type native_handle)
Definition: socket.h:105
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:352
int native_handle_type
Definition: socket_constants.h:50
const const_buffer * buffer_sequence_end(const const_buffer &b) noexcept
Definition: buffer.h:184
const const_buffer * buffer_sequence_begin(const const_buffer &b) noexcept
Definition: buffer.h:179
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:63
Definition: varlen_sort.h:174
stdx::expected< int, std::error_code > open(const char *fname, int flags, mode_t mode) noexcept
Definition: file_handle.cc:78
native_handle_type native_handle()
Definition: process.h:55
constexpr auto make_unexpected(E &&e) -> unexpected< std::decay_t< E > >
Definition: expected.h:124
required string type
Definition: replication_group_member_actions.proto:33
#define HANDLE
Definition: violite.h:158