MySQL 9.1.0
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
121 return stdx::unexpected(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()) {
185 }
186 protocol_ = protocol;
187 native_handle_ = native_handle;
188
189 return {};
190 }
191
193 DWORD wait_mode = v ? PIPE_NOWAIT : PIPE_WAIT;
194
195 bool success =
196 SetNamedPipeHandleState(native_handle_, &wait_mode, nullptr, nullptr);
197
198 if (!success) {
200 }
201
202 return {};
203 }
204
205 stdx::expected<void, std::error_code> connect(const endpoint_type &ep) {
206 if (is_open()) {
208 }
209
210 using clock_type = std::chrono::steady_clock;
211 using namespace std::chrono_literals;
212 const auto retry_step = 10ms;
213 const auto end_time = clock_type::now() + 1s;
214 do {
215 native_handle_ =
216 CreateFile(TEXT(ep.path().c_str()), GENERIC_READ | GENERIC_WRITE, 0,
217 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
218
219 if (native_handle_ == impl::named_pipe::kInvalidHandle) {
220 auto const error_code = win32::last_error_code();
221 // if the pipe is not ready, try waiting max 1 second for it to become
222 // available
223 const std::error_code ec_pipe_busy{ERROR_PIPE_BUSY,
224 std::system_category()};
225
226 if ((error_code == ec_pipe_busy) && clock_type::now() < end_time) {
227 std::this_thread::sleep_for(retry_step);
228 continue;
229 }
230
231 return stdx::unexpected(error_code);
232 } else
233 break;
234 } while (true);
235
236 return {};
237 }
238
239 private:
240 protocol_type protocol_{endpoint_type{}.protocol()};
241};
242
243/**
244 * base-class of basic_named_pipe_socket.
245 *
246 * provides functionality that's common between socket and acceptor like:
247 *
248 * - read_some
249 * - write_some
250 */
251template <class Protocol>
252class basic_named_pipe : private basic_named_pipe_impl<Protocol> {
253 private:
254 using __base = basic_named_pipe_impl<Protocol>;
255
256 public:
257 using protocol_type = Protocol;
258 using executor_type = typename __base::executor_type;
260 using endpoint_type = typename protocol_type::endpoint;
261
262 // constructors are protected, below.
263
264 executor_type get_executor() noexcept { return __base::get_executor(); }
265
267 const protocol_type &protocol, const native_handle_type &native_handle) {
268 return __base::assign(protocol, native_handle);
269 }
270
272
273 constexpr bool is_open() const noexcept { return __base::is_open(); }
274
278 }
279
282 }
283
284 stdx::expected<void, std::error_code> connect(const endpoint_type &ep) {
285 return __base::connect(ep);
286 }
287
288 template <class MutableBufferSequence>
290 const MutableBufferSequence &buffers) {
291 size_t transferred{};
292 const auto bufend = buffer_sequence_end(buffers);
293 for (auto bufcur = buffer_sequence_begin(buffers); bufcur != bufend;
294 ++bufcur) {
295 DWORD numRead{0};
296 if (!ReadFile(native_handle(), bufcur->data(), bufcur->size(), &numRead,
297 nullptr)) {
299 }
300
301 transferred += numRead;
302
303 // for now, only process the first buffer as it simplifies the
304 // error-handling after ReadFile().
305 break;
306 }
307
308 return transferred;
309 }
310
311 template <class ConstBufferSequence>
313 const ConstBufferSequence &buffers) {
314 size_t transferred{};
315
316 const auto bufend = buffer_sequence_end(buffers);
317 for (auto bufcur = buffer_sequence_begin(buffers); bufcur != bufend;
318 ++bufcur) {
319 DWORD written{0};
320
321 if (!WriteFile(native_handle(), bufcur->data(), bufcur->size(), &written,
322 nullptr)) {
324 }
325
326 transferred += written;
327
328 // for now, only process the first buffer.
329 break;
330 }
331
332 return transferred;
333 }
334
335 protected:
336 basic_named_pipe(net::io_context &ctx) : __base{ctx} {}
337
338 basic_named_pipe(net::io_context &ctx, const protocol_type &proto)
339 : __base{ctx, proto} {}
340
341 basic_named_pipe(net::io_context &ctx, const protocol_type &proto,
343 : __base{ctx} {
344 assign(proto, native_handle);
345 }
346
347 basic_named_pipe(const basic_named_pipe &) = delete;
348 basic_named_pipe &operator=(const basic_named_pipe &) = delete;
349 basic_named_pipe(basic_named_pipe &&) = default;
350 basic_named_pipe &operator=(basic_named_pipe &&) = default;
351
352 ~basic_named_pipe() = default;
353};
354
355/**
356 * client side of a basic_named_pipe.
357 *
358 * @tparam Protocol a protocol class that provides .protocol() and .type()
359 */
360template <class Protocol>
361class basic_named_pipe_socket : public basic_named_pipe<Protocol> {
362 private:
363 using __base = basic_named_pipe<Protocol>;
364
365 public:
366 using protocol_type = Protocol;
368 using endpoint_type = typename protocol_type::endpoint;
369
370 explicit basic_named_pipe_socket(net::io_context &ctx) : __base(ctx) {}
371 basic_named_pipe_socket(net::io_context &ctx, const protocol_type &proto)
372 : __base(ctx, proto) {}
373
374 basic_named_pipe_socket(net::io_context &ctx, const protocol_type &proto,
376 : __base(ctx, proto, native_handle) {}
377
378 basic_named_pipe_socket(const basic_named_pipe_socket &) = delete;
379 basic_named_pipe_socket &operator=(const basic_named_pipe_socket &) = delete;
380
381 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
382 basic_named_pipe_socket(basic_named_pipe_socket &&other) = default;
383 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
384 basic_named_pipe_socket &operator=(basic_named_pipe_socket &&) = default;
385
386 ~basic_named_pipe_socket() {
387 if (is_open()) close();
388 }
389
390 constexpr bool is_open() const noexcept { return __base::is_open(); }
391
393
395 if (is_open()) {
397 }
398
399 return {};
400 }
401
403 if (is_open()) {
404 DisconnectNamedPipe(native_handle());
406 }
407
408 return {};
409 }
410
411 template <class ConstBufferSequence>
413 const ConstBufferSequence &buffers) {
414 return __base::write_some(buffers);
415 }
416
418 if (is_open()) {
420 } else {
421 native_non_blocking_ = static_cast<int>(v);
422
423 return {};
424 }
425 }
426
427 stdx::expected<void, std::error_code> connect(const endpoint_type &ep) {
428 auto connect_res = __base::connect(ep);
429 if (!connect_res) return connect_res;
430
431 if (native_non_blocking_ == 1) {
433 }
434
435 return connect_res;
436 }
437
438 private:
439 int native_non_blocking_{-1};
440};
441
442/**
443 * server side of a named pipe.
444 *
445 * can accept a connection from an named pipe path.
446 */
447template <class Protocol>
448class basic_named_pipe_acceptor : public basic_named_pipe_impl<Protocol> {
449 private:
450 using __base = basic_named_pipe_impl<Protocol>;
451
452 public:
453 using protocol_type = Protocol;
454 using socket_type = basic_named_pipe_socket<protocol_type>;
455 using endpoint_type = typename protocol_type::endpoint;
456 using executor_type = typename __base::executor_type;
458
459 explicit basic_named_pipe_acceptor(net::io_context &ctx) : __base(ctx) {}
460 basic_named_pipe_acceptor(net::io_context &ctx, const protocol_type &proto)
461 : __base(ctx, proto) {}
462
463 basic_named_pipe_acceptor(const basic_named_pipe_acceptor &) = delete;
464 basic_named_pipe_acceptor &operator=(const basic_named_pipe_acceptor &) =
465 delete;
466
467 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
468 basic_named_pipe_acceptor(basic_named_pipe_acceptor &&other) = default;
469 // NOLINTNEXTLINE(hicpp-noexcept-move,performance-noexcept-move-constructor)
470 basic_named_pipe_acceptor &operator=(basic_named_pipe_acceptor &&) = default;
471
472 ~basic_named_pipe_acceptor() {
473 if (is_open()) close();
474 }
475
476 executor_type get_executor() noexcept { return __base::get_executor(); }
477
478 bool is_open() const { return __base::is_open(); }
479
481
484 }
485
487
489 if (is_open()) {
490 CloseHandle(native_handle());
492 }
493
494 return {};
495 }
496
498 return __base::release();
499 }
500
502 if (is_open()) {
504 } else {
505 native_non_blocking_ = static_cast<int>(v);
506
507 return {};
508 }
509 }
510
511 /**
512 * bind endpoint to socket.
513 *
514 * in accordance with net-ts' acceptors' bind().
515 *
516 * @param ep an endpoint this method binds to a socket
517 * @param flags flags passed to CreateNamedPipe() as is. Use for PIPE_NOWAIT.
518 *
519 * @retval std::errc::invalid_argument if ep.path() is empty
520 * @retval std::errc::invalid_argument if already bound.
521 */
522 stdx::expected<void, std::error_code> bind(const endpoint_type &ep,
523 int flags = 0) {
524 if (ep.path().empty()) {
525 return stdx::unexpected(make_error_code(std::errc::invalid_argument));
526 }
527
528 // already bound.
529 if (!ep_.path().empty()) {
530 return stdx::unexpected(make_error_code(std::errc::invalid_argument));
531 }
532
533 ep_ = ep;
534
535 const auto protocol = protocol_type();
536
537 if (native_non_blocking_ == 1) flags |= PIPE_NOWAIT;
538
539 if (!is_open()) {
540 auto handle = CreateNamedPipe(
541 TEXT(ep_.path().c_str()), PIPE_ACCESS_DUPLEX,
542 protocol.type() | protocol.protocol() | PIPE_REJECT_REMOTE_CLIENTS |
543 flags,
544 back_log_, 1024 * 16, 1024 * 16, NMPWAIT_USE_DEFAULT_WAIT, nullptr);
545
547 auto ec = win32::last_error_code();
548 return stdx::unexpected(ec);
549 }
550
552 }
553
554 return {};
555 }
556
557 /**
558 * max waiting pending connections.
559 */
561 int back_log = PIPE_UNLIMITED_INSTANCES) {
562 if (back_log <= 0) {
563 return stdx::unexpected(make_error_code(std::errc::invalid_argument));
564 }
565 if (back_log > PIPE_UNLIMITED_INSTANCES) {
566 return stdx::unexpected(make_error_code(std::errc::invalid_argument));
567 }
568
569 back_log_ = back_log;
570
571 return {};
572 }
573
574 /**
575 * accept a client connection.
576 *
577 * - CreateNamedPipe() on the endpoint assigned by bind()
578 * - ConnectNamedPipe() to accept the client connection.
579 *
580 * @return a connected named pipe.
581 * @retval std::errc::invalid_argument if no endpoint bound.
582 */
585
586 // not bound.
587 if (ep_.path().empty()) {
588 return stdx::unexpected(make_error_code(std::errc::invalid_argument));
589 }
590
591 const auto protocol = protocol_type();
592
593 const bool connected = ConnectNamedPipe(native_handle(), nullptr);
594 if (!connected) {
595 auto last_ec = win32::last_error_code();
596
597 const std::error_code ec_pipe_connected{ERROR_PIPE_CONNECTED, // 535
598 std::system_category()};
599 const std::error_code ec_no_data{ERROR_NO_DATA, // 232
600 std::system_category()};
601
602 // ERROR_PIPE_CONNECTED is also a success
603 // ERROR_NO_DATA too, it just means the pipe is already closed, but quite
604 // likely readable.
605 if (last_ec == ec_pipe_connected || last_ec == ec_no_data) {
606 return ret_type{std::in_place, get_executor().context(), protocol,
607 native_handle()};
608 }
609
610 return stdx::unexpected(last_ec);
611 }
612
613 return ret_type{std::in_place, get_executor().context(), protocol,
614 native_handle()};
615 }
616
618 return ep_;
619 }
620
621 private:
622 endpoint_type ep_;
623 int back_log_{PIPE_UNLIMITED_INSTANCES};
624 int native_non_blocking_{-1}; // unset.
625};
626
627/**
628 * endpoint of a named pipe.
629 */
630template <typename Protocol>
631class basic_named_pipe_endpoint {
632 public:
633 using protocol_type = Protocol;
634
635 basic_named_pipe_endpoint() = default;
636 basic_named_pipe_endpoint(std::string path)
637 : path_{path.substr(0, std::min(max_path_len(), path.size()))} {}
638
639 std::string path() const { return path_; }
640
641 constexpr protocol_type protocol() const noexcept { return protocol_type(); }
642
643 size_t size() const { return path().size(); }
644
645 size_t capacity() const { return max_path_len(); }
646
647 void resize(size_t size) { path_.resize(size); }
648
649 private:
650 constexpr size_t max_path_len() const { return 256; }
651
652 std::string path_;
653};
654
655/**
656 * protocol class for message oriented named pipes.
657 */
658class message_protocol {
659 public:
660 using endpoint = basic_named_pipe_endpoint<message_protocol>;
661 using socket = basic_named_pipe_socket<message_protocol>;
662 using acceptor = basic_named_pipe_acceptor<message_protocol>;
663
664 int type() const { return PIPE_TYPE_MESSAGE; }
665 int protocol() const { return PIPE_READMODE_MESSAGE; }
666};
667
668/**
669 * protocol class for byte oriented named pipes.
670 */
671class byte_protocol {
672 public:
673 using endpoint = basic_named_pipe_endpoint<byte_protocol>;
674 using socket = basic_named_pipe_socket<byte_protocol>;
675 using acceptor = basic_named_pipe_acceptor<byte_protocol>;
676
677 int type() const { return PIPE_TYPE_BYTE; }
678 int protocol() const { return PIPE_READMODE_BYTE; }
679};
680
681} // namespace local
682
683#endif
684
685#endif
Definition: protocol.h:33
Definition: io_context.h:991
Definition: io_context.h:61
Definition: expected.h:286
static int flags[50]
Definition: hp_test1.cc:40
static bool connected
Definition: mysql.cc:167
ulong back_log
Definition: mysqld.cc:1347
static char * path
Definition: mysqldump.cc:149
Definition: http_server_component.cc:34
Definition: local.h:60
std::error_code make_error_code(DynamicLoaderErrc ec)
make error_code from a DynamicLoaderErrc.
Definition: dynamic_loader.cc:79
size_t size(const char *const c)
Definition: base64.h:46
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
unexpected(E) -> unexpected< E >
required string type
Definition: replication_group_member_actions.proto:34
#define HANDLE
Definition: violite.h:159