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 DisconnectNamedPipe(s
->fd
);
189 CloseHandle(s
->read
.hEvent
);
190 CloseHandle(s
->write
.hEvent
);
197 /* Active named pipe connect. */
199 windows_connect(struct stream
*stream
)
201 struct windows_stream
*s
= stream_windows_cast(stream
);
203 if (!s
->retry_connect
) {
207 npipe
= create_snpipe(s
->pipe_path
);
208 if (npipe
== INVALID_HANDLE_VALUE
) {
209 if (GetLastError() == ERROR_PIPE_BUSY
) {
212 s
->retry_connect
= false;
216 s
->retry_connect
= false;
222 /* Active named pipe receive. */
224 windows_recv(struct stream
*stream
, void *buffer
, size_t n
)
226 struct windows_stream
*s
= stream_windows_cast(stream
);
228 boolean result
= false;
229 DWORD last_error
= 0;
230 LPOVERLAPPED ov
= NULL
;
233 /* If the read operation was pending, we verify its result. */
234 if (s
->read_pending
) {
235 if (!GetOverlappedResult(s
->fd
, ov
, &(DWORD
)retval
, FALSE
)) {
236 last_error
= GetLastError();
237 if (last_error
== ERROR_IO_INCOMPLETE
) {
238 /* If the operation is still pending, retry again. */
239 s
->read_pending
= true;
241 } else if (last_error
== ERROR_PIPE_NOT_CONNECTED
242 || last_error
== ERROR_BAD_PIPE
243 || last_error
== ERROR_NO_DATA
244 || last_error
== ERROR_BROKEN_PIPE
) {
245 /* If the pipe was disconnected, return 0. */
248 VLOG_ERR_RL(&rl
, "Could not receive data on named pipe. Last "
249 "error: %s", ovs_lasterror_to_string());
253 s
->read_pending
= false;
257 result
= ReadFile(s
->fd
, buffer
, n
, &(DWORD
)retval
, ov
);
259 if (!result
&& GetLastError() == ERROR_IO_PENDING
) {
260 /* Mark the read operation as pending. */
261 s
->read_pending
= true;
263 } else if (!result
) {
264 last_error
= GetLastError();
265 if (last_error
== ERROR_PIPE_NOT_CONNECTED
266 || last_error
== ERROR_BAD_PIPE
267 || last_error
== ERROR_NO_DATA
268 || last_error
== ERROR_BROKEN_PIPE
) {
269 /* If the pipe was disconnected, return 0. */
272 VLOG_ERR_RL(&rl
, "Could not receive data synchronous on named pipe."
273 "Last error: %s", ovs_lasterror_to_string());
280 /* Active named pipe send. */
282 windows_send(struct stream
*stream
, const void *buffer
, size_t n
)
284 struct windows_stream
*s
= stream_windows_cast(stream
);
286 boolean result
= false;
287 DWORD last_error
= 0;
288 LPOVERLAPPED ov
= NULL
;
291 /* If the send operation was pending, we verify the result. */
292 if (s
->write_pending
) {
293 if (!GetOverlappedResult(s
->fd
, ov
, &(DWORD
)retval
, FALSE
)) {
294 last_error
= GetLastError();
295 if (last_error
== ERROR_IO_INCOMPLETE
) {
296 /* If the operation is still pending, retry again. */
297 s
->write_pending
= true;
299 } else if (last_error
== ERROR_PIPE_NOT_CONNECTED
300 || last_error
== ERROR_BAD_PIPE
301 || last_error
== ERROR_NO_DATA
302 || last_error
== ERROR_BROKEN_PIPE
) {
303 /* If the pipe was disconnected, return connection reset. */
304 return -WSAECONNRESET
;
306 VLOG_ERR_RL(&rl
, "Could not send data on named pipe. Last "
307 "error: %s", ovs_lasterror_to_string());
311 s
->write_pending
= false;
315 result
= WriteFile(s
->fd
, buffer
, n
, &(DWORD
)retval
, ov
);
316 last_error
= GetLastError();
317 if (!result
&& GetLastError() == ERROR_IO_PENDING
) {
318 /* Mark the send operation as pending. */
319 s
->write_pending
= true;
321 } else if (!result
&& (last_error
== ERROR_PIPE_NOT_CONNECTED
322 || last_error
== ERROR_BAD_PIPE
323 || last_error
== ERROR_NO_DATA
324 || last_error
== ERROR_BROKEN_PIPE
)) {
325 /* If the pipe was disconnected, return connection reset. */
326 return -WSAECONNRESET
;
327 } else if (!result
) {
328 VLOG_ERR_RL(&rl
, "Could not send data on synchronous named pipe. Last "
329 "error: %s", ovs_lasterror_to_string());
332 return (retval
> 0 ? retval
: -EAGAIN
);
335 /* Active named pipe wait. */
337 windows_wait(struct stream
*stream
, enum stream_wait_type wait
)
339 struct windows_stream
*s
= stream_windows_cast(stream
);
342 poll_wevent_wait(s
->write
.hEvent
);
346 poll_immediate_wake();
350 poll_wevent_wait(s
->read
.hEvent
);
358 /* Passive named pipe. */
359 const struct stream_class windows_stream_class
= {
361 false, /* needs_probes */
362 windows_open
, /* open */
363 windows_close
, /* close */
364 windows_connect
, /* connect */
365 windows_recv
, /* recv */
366 windows_send
, /* send */
369 windows_wait
, /* wait */
372 struct pwindows_pstream
374 struct pstream pstream
;
376 /* Unlink path to be deleted during close. */
378 /* Overlapped operation used for connect. */
380 /* Flag to check if an operation is pending. */
382 /* String used to create the named pipe. */
386 const struct pstream_class pwindows_pstream_class
;
388 static struct pwindows_pstream
*
389 pwindows_pstream_cast(struct pstream
*pstream
)
391 pstream_assert_class(pstream
, &pwindows_pstream_class
);
392 return CONTAINER_OF(pstream
, struct pwindows_pstream
, pstream
);
395 /* Create a named pipe with read/write access, overlapped, message mode for
396 * writing, byte mode for reading and with a maximum of 64 active instances. */
398 create_pnpipe(char *name
)
400 SECURITY_ATTRIBUTES sa
;
401 sa
.nLength
= sizeof(sa
);
402 sa
.lpSecurityDescriptor
= NULL
;
403 sa
.bInheritHandle
= TRUE
;
404 if (strlen(name
) > 256) {
405 VLOG_ERR_RL(&rl
, "Named pipe name too long.");
406 return INVALID_HANDLE_VALUE
;
408 return CreateNamedPipe(name
, PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
409 PIPE_TYPE_MESSAGE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
410 64, BUFSIZE
, BUFSIZE
, 0, &sa
);
413 /* Passive named pipe connect. This function creates a new named pipe and
414 * passes the old handle to the active stream. */
416 pwindows_accept(struct pstream
*pstream
, struct stream
**new_streamp
)
418 struct pwindows_pstream
*p
= pwindows_pstream_cast(pstream
);
419 DWORD last_error
= 0;
423 /* If the connect operation was pending, verify the result. */
425 if (!GetOverlappedResult(p
->fd
, &p
->connect
, &cbRet
, FALSE
)) {
426 last_error
= GetLastError();
427 if (last_error
== ERROR_IO_INCOMPLETE
) {
428 /* If the operation is still pending, retry again. */
432 VLOG_ERR_RL(&rl
, "Could not connect named pipe. Last "
433 "error: %s", ovs_lasterror_to_string());
434 DisconnectNamedPipe(p
->fd
);
441 if (!p
->pending
&& !ConnectNamedPipe(p
->fd
, &p
->connect
)) {
442 last_error
= GetLastError();
443 if (last_error
== ERROR_IO_PENDING
) {
444 /* Mark the accept operation as pending. */
447 } else if (last_error
!= ERROR_PIPE_CONNECTED
) {
448 VLOG_ERR_RL(&rl
, "Could not connect synchronous named pipe. Last "
449 "error: %s", ovs_lasterror_to_string());
450 DisconnectNamedPipe(p
->fd
);
453 /* If the pipe is connected, signal an event. */
454 SetEvent(&p
->connect
.hEvent
);
458 npipe
= create_pnpipe(p
->pipe_path
);
459 if (npipe
== INVALID_HANDLE_VALUE
) {
460 VLOG_ERR_RL(&rl
, "Could not create a new named pipe after connect. ",
461 ovs_lasterror_to_string());
465 /* Give the handle p->fd to the new created active stream and specify it
466 * was created by an active stream. */
467 struct windows_stream
*p_temp
= xmalloc(sizeof *p_temp
);
468 stream_init(&p_temp
->stream
, &windows_stream_class
, 0, "unix");
470 /* Specify it was created by a passive stream. */
471 p_temp
->server
= true;
472 /* Create events for read/write operations. */
473 memset(&p_temp
->read
, 0, sizeof(p_temp
->read
));
474 memset(&p_temp
->write
, 0, sizeof(p_temp
->write
));
475 p_temp
->read
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
476 p_temp
->write
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
477 p_temp
->read_pending
= false;
478 p_temp
->write_pending
= false;
479 p_temp
->retry_connect
= false;
480 p_temp
->pipe_path
= NULL
;
481 *new_streamp
= &p_temp
->stream
;
483 /* The passive handle p->fd will be the new created handle. */
485 memset(&p
->connect
, 0, sizeof(p
->connect
));
486 p
->connect
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
491 /* Passive named pipe close. */
493 pwindows_close(struct pstream
*pstream
)
495 struct pwindows_pstream
*p
= pwindows_pstream_cast(pstream
);
496 DisconnectNamedPipe(p
->fd
);
498 CloseHandle(p
->connect
.hEvent
);
499 maybe_unlink_and_free(p
->unlink_path
);
504 /* Passive named pipe wait. */
506 pwindows_wait(struct pstream
*pstream
)
508 struct pwindows_pstream
*p
= pwindows_pstream_cast(pstream
);
509 poll_wevent_wait(p
->connect
.hEvent
);
512 /* Passive named pipe. */
514 pwindows_open(const char *name OVS_UNUSED
, char *suffix
,
515 struct pstream
**pstreamp
, uint8_t dscp OVS_UNUSED
)
523 if (!strchr(suffix
, ':')) {
524 path
= xasprintf("%s/%s", ovs_rundir(), suffix
);
526 path
= xstrdup(suffix
);
529 /* Try to create a file under the path location. */
530 FILE *file
= fopen(path
, "w");
534 VLOG_DBG_RL(&rl
, "could not open %s (%s)", path
, ovs_strerror(error
));
540 orig_path
= xstrdup(path
);
541 /* Strip slashes from path and create a named pipe using that newly created
543 bind_path
= xasprintf("%s%s", LOCAL_PREFIX
, remove_slashes(path
));
546 npipe
= create_pnpipe(bind_path
);
548 if (npipe
== INVALID_HANDLE_VALUE
) {
549 VLOG_ERR_RL(&rl
, "Could not create named pipe. Last error: %s",
550 ovs_lasterror_to_string());
554 struct pwindows_pstream
*p
= xmalloc(sizeof *p
);
555 pstream_init(&p
->pstream
, &pwindows_pstream_class
, name
);
557 p
->unlink_path
= orig_path
;
558 memset(&p
->connect
, 0, sizeof(p
->connect
));
559 p
->connect
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
561 p
->pipe_path
= bind_path
;
562 *pstreamp
= &p
->pstream
;
566 const struct pstream_class pwindows_pstream_class
= {
569 pwindows_open
, /* open */
570 pwindows_close
, /* close */
571 pwindows_accept
, /* accept */
572 pwindows_wait
, /* wait */
575 /* Helper functions. */
577 maybe_unlink_and_free(char *path
)
580 fatal_signal_unlink_file_now(path
);