2 * Copyright (c) 2016 Cloudbase Solutions Srl
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <sys/types.h>
23 #include "poll-loop.h"
26 #include "stream-provider.h"
27 #include "openvswitch/vlog.h"
29 VLOG_DEFINE_THIS_MODULE(stream_windows
);
31 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(10, 25);
33 static void maybe_unlink_and_free(char *path
);
35 /* Suggested buffer size at the creation of the named pipe for reading and
36 * and writing operations. */
39 /* Default prefix of a local named pipe. */
40 #define LOCAL_PREFIX "\\\\.\\pipe\\"
42 /* This function has the purpose to remove all the slashes received in s. */
44 remove_slashes(char *s
)
50 if ((*p1
) == '\\' || (*p1
) == '/') {
62 /* Active named pipe. */
67 /* Overlapped operations used for reading/writing. */
70 /* Flag to check if a reading/writing operation is pending. */
73 /* Flag to check if fd is a server HANDLE. In the case of a server handle
74 * we have to issue a disconnect before closing the actual handle. */
80 static struct windows_stream
*
81 stream_windows_cast(struct stream
*stream
)
83 stream_assert_class(stream
, &windows_stream_class
);
84 return CONTAINER_OF(stream
, struct windows_stream
, stream
);
88 create_snpipe(char *path
)
90 return CreateFile(path
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
92 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_OVERLAPPED
|
93 FILE_FLAG_NO_BUFFERING
,
97 /* Active named pipe open. */
99 windows_open(const char *name
, char *suffix
, struct stream
**streamp
,
100 uint8_t dscp OVS_UNUSED
)
104 DWORD mode
= PIPE_READMODE_BYTE
;
108 /* If the path does not contain a ':', assume it is relative to
110 if (!strchr(suffix
, ':')) {
111 path
= xasprintf("%s/%s", ovs_rundir(), suffix
);
113 path
= xstrdup(suffix
);
116 /* In case of "unix:" argument, the assumption is that there is a file
117 * created in the path (name). */
118 file
= fopen(path
, "r");
121 VLOG_DBG_RL(&rl
, "%s: could not open %s (%s)", name
, suffix
,
122 ovs_strerror(errno
));
128 /* Valid pipe names do not have slashes. The assumption is that the named
129 * pipe was created with the name "path", with slashes removed and the
130 * default prefix \\.\pipe\ appended.
131 * Strip the slashes from the parameter name and append the default prefix.
133 connect_path
= xasprintf("%s%s", LOCAL_PREFIX
, remove_slashes(path
));
136 /* Try to connect to the named pipe. In case all pipe instances are
137 * busy we set the retry flag to true and retry again during the
138 * connect function. Use overlapped flag and file no buffering to ensure
139 * asynchronous operations. */
140 npipe
= create_snpipe(connect_path
);
142 if (npipe
== INVALID_HANDLE_VALUE
&& GetLastError() == ERROR_PIPE_BUSY
) {
146 if (!retry
&& npipe
== INVALID_HANDLE_VALUE
) {
147 VLOG_ERR_RL(&rl
, "Could not connect to named pipe: %s",
148 ovs_lasterror_to_string());
152 if (!retry
&& !SetNamedPipeHandleState(npipe
, &mode
, NULL
, NULL
)) {
153 VLOG_ERR_RL(&rl
, "Could not set named pipe options: %s",
154 ovs_lasterror_to_string());
159 struct windows_stream
*s
= xmalloc(sizeof *s
);
160 stream_init(&s
->stream
, &windows_stream_class
, 0, name
);
161 s
->pipe_path
= connect_path
;
163 /* This is an active stream. */
165 /* Create events for reading and writing to be signaled later. */
166 memset(&s
->read
, 0, sizeof(s
->read
));
167 memset(&s
->write
, 0, sizeof(s
->write
));
168 s
->read
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
169 s
->write
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
170 /* Initial read and write operations are not pending. */
171 s
->read_pending
= false;
172 s
->write_pending
= false;
173 s
->retry_connect
= retry
;
174 *streamp
= &s
->stream
;
178 /* Active named pipe close. */
180 windows_close(struct stream
*stream
)
182 struct windows_stream
*s
= stream_windows_cast(stream
);
183 /* Disconnect the named pipe in case it was created from a passive stream.
186 /* Flush the pipe to allow the client to read the pipe's contents
187 * before disconnecting. */
188 FlushFileBuffers(s
->fd
);
189 DisconnectNamedPipe(s
->fd
);
192 CloseHandle(s
->read
.hEvent
);
193 CloseHandle(s
->write
.hEvent
);
200 /* Active named pipe connect. */
202 windows_connect(struct stream
*stream
)
204 struct windows_stream
*s
= stream_windows_cast(stream
);
206 if (!s
->retry_connect
) {
210 npipe
= create_snpipe(s
->pipe_path
);
211 if (npipe
== INVALID_HANDLE_VALUE
) {
212 if (GetLastError() == ERROR_PIPE_BUSY
) {
215 s
->retry_connect
= false;
219 s
->retry_connect
= false;
225 /* Active named pipe receive. */
227 windows_recv(struct stream
*stream
, void *buffer
, size_t n
)
229 struct windows_stream
*s
= stream_windows_cast(stream
);
231 boolean result
= false;
232 DWORD last_error
= 0;
233 LPOVERLAPPED ov
= NULL
;
236 /* If the read operation was pending, we verify its result. */
237 if (s
->read_pending
) {
238 if (!GetOverlappedResult(s
->fd
, ov
, &(DWORD
)retval
, FALSE
)) {
239 last_error
= GetLastError();
240 if (last_error
== ERROR_IO_INCOMPLETE
) {
241 /* If the operation is still pending, retry again. */
242 s
->read_pending
= true;
244 } else if (last_error
== ERROR_PIPE_NOT_CONNECTED
245 || last_error
== ERROR_BAD_PIPE
246 || last_error
== ERROR_NO_DATA
247 || last_error
== ERROR_BROKEN_PIPE
) {
248 /* If the pipe was disconnected, return 0. */
251 VLOG_ERR_RL(&rl
, "Could not receive data on named pipe. Last "
252 "error: %s", ovs_lasterror_to_string());
256 s
->read_pending
= false;
260 result
= ReadFile(s
->fd
, buffer
, n
, &(DWORD
)retval
, ov
);
262 if (!result
&& GetLastError() == ERROR_IO_PENDING
) {
263 /* Mark the read operation as pending. */
264 s
->read_pending
= true;
266 } else if (!result
) {
267 last_error
= GetLastError();
268 if (last_error
== ERROR_PIPE_NOT_CONNECTED
269 || last_error
== ERROR_BAD_PIPE
270 || last_error
== ERROR_NO_DATA
271 || last_error
== ERROR_BROKEN_PIPE
) {
272 /* If the pipe was disconnected, return 0. */
275 VLOG_ERR_RL(&rl
, "Could not receive data synchronous on named pipe."
276 "Last error: %s", ovs_lasterror_to_string());
283 /* Active named pipe send. */
285 windows_send(struct stream
*stream
, const void *buffer
, size_t n
)
287 struct windows_stream
*s
= stream_windows_cast(stream
);
289 boolean result
= false;
290 DWORD last_error
= 0;
291 LPOVERLAPPED ov
= NULL
;
294 /* If the send operation was pending, we verify the result. */
295 if (s
->write_pending
) {
296 if (!GetOverlappedResult(s
->fd
, ov
, &(DWORD
)retval
, FALSE
)) {
297 last_error
= GetLastError();
298 if (last_error
== ERROR_IO_INCOMPLETE
) {
299 /* If the operation is still pending, retry again. */
300 s
->write_pending
= true;
302 } else if (last_error
== ERROR_PIPE_NOT_CONNECTED
303 || last_error
== ERROR_BAD_PIPE
304 || last_error
== ERROR_NO_DATA
305 || last_error
== ERROR_BROKEN_PIPE
) {
306 /* If the pipe was disconnected, return connection reset. */
307 return -WSAECONNRESET
;
309 VLOG_ERR_RL(&rl
, "Could not send data on named pipe. Last "
310 "error: %s", ovs_lasterror_to_string());
314 s
->write_pending
= false;
318 result
= WriteFile(s
->fd
, buffer
, n
, &(DWORD
)retval
, ov
);
319 last_error
= GetLastError();
320 if (!result
&& GetLastError() == ERROR_IO_PENDING
) {
321 /* Mark the send operation as pending. */
322 s
->write_pending
= true;
324 } else if (!result
&& (last_error
== ERROR_PIPE_NOT_CONNECTED
325 || last_error
== ERROR_BAD_PIPE
326 || last_error
== ERROR_NO_DATA
327 || last_error
== ERROR_BROKEN_PIPE
)) {
328 /* If the pipe was disconnected, return connection reset. */
329 return -WSAECONNRESET
;
330 } else if (!result
) {
331 VLOG_ERR_RL(&rl
, "Could not send data on synchronous named pipe. Last "
332 "error: %s", ovs_lasterror_to_string());
335 return (retval
> 0 ? retval
: -EAGAIN
);
338 /* Active named pipe wait. */
340 windows_wait(struct stream
*stream
, enum stream_wait_type wait
)
342 struct windows_stream
*s
= stream_windows_cast(stream
);
345 poll_wevent_wait(s
->write
.hEvent
);
349 poll_immediate_wake();
353 poll_wevent_wait(s
->read
.hEvent
);
361 /* Passive named pipe. */
362 const struct stream_class windows_stream_class
= {
364 false, /* needs_probes */
365 windows_open
, /* open */
366 windows_close
, /* close */
367 windows_connect
, /* connect */
368 windows_recv
, /* recv */
369 windows_send
, /* send */
372 windows_wait
, /* wait */
375 struct pwindows_pstream
377 struct pstream pstream
;
379 /* Unlink path to be deleted during close. */
381 /* Overlapped operation used for connect. */
383 /* Flag to check if an operation is pending. */
385 /* String used to create the named pipe. */
389 const struct pstream_class pwindows_pstream_class
;
391 static struct pwindows_pstream
*
392 pwindows_pstream_cast(struct pstream
*pstream
)
394 pstream_assert_class(pstream
, &pwindows_pstream_class
);
395 return CONTAINER_OF(pstream
, struct pwindows_pstream
, pstream
);
398 /* Create a named pipe with read/write access, overlapped, message mode for
399 * writing, byte mode for reading and with a maximum of 64 active instances. */
401 create_pnpipe(char *name
)
403 SECURITY_ATTRIBUTES sa
;
404 sa
.nLength
= sizeof(sa
);
405 sa
.lpSecurityDescriptor
= NULL
;
406 sa
.bInheritHandle
= TRUE
;
407 if (strlen(name
) > 256) {
408 VLOG_ERR_RL(&rl
, "Named pipe name too long.");
409 return INVALID_HANDLE_VALUE
;
411 return CreateNamedPipe(name
, PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
412 PIPE_TYPE_MESSAGE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
413 64, BUFSIZE
, BUFSIZE
, 0, &sa
);
416 /* Passive named pipe connect. This function creates a new named pipe and
417 * passes the old handle to the active stream. */
419 pwindows_accept(struct pstream
*pstream
, struct stream
**new_streamp
)
421 struct pwindows_pstream
*p
= pwindows_pstream_cast(pstream
);
422 DWORD last_error
= 0;
426 /* If the connect operation was pending, verify the result. */
428 if (!GetOverlappedResult(p
->fd
, &p
->connect
, &cbRet
, FALSE
)) {
429 last_error
= GetLastError();
430 if (last_error
== ERROR_IO_INCOMPLETE
) {
431 /* If the operation is still pending, retry again. */
435 VLOG_ERR_RL(&rl
, "Could not connect named pipe. Last "
436 "error: %s", ovs_lasterror_to_string());
437 DisconnectNamedPipe(p
->fd
);
444 if (!p
->pending
&& !ConnectNamedPipe(p
->fd
, &p
->connect
)) {
445 last_error
= GetLastError();
446 if (last_error
== ERROR_IO_PENDING
) {
447 /* Mark the accept operation as pending. */
450 } else if (last_error
!= ERROR_PIPE_CONNECTED
) {
451 VLOG_ERR_RL(&rl
, "Could not connect synchronous named pipe. Last "
452 "error: %s", ovs_lasterror_to_string());
453 DisconnectNamedPipe(p
->fd
);
456 /* If the pipe is connected, signal an event. */
457 SetEvent(&p
->connect
.hEvent
);
461 npipe
= create_pnpipe(p
->pipe_path
);
462 if (npipe
== INVALID_HANDLE_VALUE
) {
463 VLOG_ERR_RL(&rl
, "Could not create a new named pipe after connect. ",
464 ovs_lasterror_to_string());
468 /* Give the handle p->fd to the new created active stream and specify it
469 * was created by an active stream. */
470 struct windows_stream
*p_temp
= xmalloc(sizeof *p_temp
);
471 stream_init(&p_temp
->stream
, &windows_stream_class
, 0, "unix");
473 /* Specify it was created by a passive stream. */
474 p_temp
->server
= true;
475 /* Create events for read/write operations. */
476 memset(&p_temp
->read
, 0, sizeof(p_temp
->read
));
477 memset(&p_temp
->write
, 0, sizeof(p_temp
->write
));
478 p_temp
->read
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
479 p_temp
->write
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
480 p_temp
->read_pending
= false;
481 p_temp
->write_pending
= false;
482 p_temp
->retry_connect
= false;
483 p_temp
->pipe_path
= NULL
;
484 *new_streamp
= &p_temp
->stream
;
486 /* The passive handle p->fd will be the new created handle. */
488 memset(&p
->connect
, 0, sizeof(p
->connect
));
489 p
->connect
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
494 /* Passive named pipe close. */
496 pwindows_close(struct pstream
*pstream
)
498 struct pwindows_pstream
*p
= pwindows_pstream_cast(pstream
);
499 DisconnectNamedPipe(p
->fd
);
501 CloseHandle(p
->connect
.hEvent
);
502 maybe_unlink_and_free(p
->unlink_path
);
507 /* Passive named pipe wait. */
509 pwindows_wait(struct pstream
*pstream
)
511 struct pwindows_pstream
*p
= pwindows_pstream_cast(pstream
);
512 poll_wevent_wait(p
->connect
.hEvent
);
515 /* Passive named pipe. */
517 pwindows_open(const char *name OVS_UNUSED
, char *suffix
,
518 struct pstream
**pstreamp
, uint8_t dscp OVS_UNUSED
)
526 if (!strchr(suffix
, ':')) {
527 path
= xasprintf("%s/%s", ovs_rundir(), suffix
);
529 path
= xstrdup(suffix
);
532 /* Try to create a file under the path location. */
533 FILE *file
= fopen(path
, "w");
537 VLOG_DBG_RL(&rl
, "could not open %s (%s)", path
, ovs_strerror(error
));
543 orig_path
= xstrdup(path
);
544 /* Strip slashes from path and create a named pipe using that newly created
546 bind_path
= xasprintf("%s%s", LOCAL_PREFIX
, remove_slashes(path
));
549 npipe
= create_pnpipe(bind_path
);
551 if (npipe
== INVALID_HANDLE_VALUE
) {
552 VLOG_ERR_RL(&rl
, "Could not create named pipe. Last error: %s",
553 ovs_lasterror_to_string());
557 struct pwindows_pstream
*p
= xmalloc(sizeof *p
);
558 pstream_init(&p
->pstream
, &pwindows_pstream_class
, name
);
560 p
->unlink_path
= orig_path
;
561 memset(&p
->connect
, 0, sizeof(p
->connect
));
562 p
->connect
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
564 p
->pipe_path
= bind_path
;
565 *pstreamp
= &p
->pstream
;
569 const struct pstream_class pwindows_pstream_class
= {
572 pwindows_open
, /* open */
573 pwindows_close
, /* close */
574 pwindows_accept
, /* accept */
575 pwindows_wait
, /* wait */
578 /* Helper functions. */
580 maybe_unlink_and_free(char *path
)
583 fatal_signal_unlink_file_now(path
);