summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/automake.mk1
-rw-r--r--lib/stream-tcp.c115
-rw-r--r--lib/stream-windows.c581
-rw-r--r--lib/unixctl.c5
-rw-r--r--lib/unixctl.man10
-rw-r--r--lib/vconn-active.man4
-rw-r--r--ovsdb/remote-active.man5
-rw-r--r--ovsdb/remote-passive.man4
-rw-r--r--tests/ovsdb-server.at6
9 files changed, 601 insertions, 130 deletions
diff --git a/lib/automake.mk b/lib/automake.mk
index 646306deb..97c83e9c3 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -302,6 +302,7 @@ lib_libopenvswitch_la_SOURCES += \
lib/latch-windows.c \
lib/route-table-stub.c \
lib/if-notifier-stub.c \
+ lib/stream-windows.c \
lib/strsep.c
else
lib_libopenvswitch_la_SOURCES += \
diff --git a/lib/stream-tcp.c b/lib/stream-tcp.c
index 2b57ca71a..1749fadbf 100644
--- a/lib/stream-tcp.c
+++ b/lib/stream-tcp.c
@@ -74,64 +74,6 @@ const struct stream_class tcp_stream_class = {
NULL, /* run_wait */
NULL, /* wait */
};
-
-#ifdef _WIN32
-#include "dirs.h"
-
-static int
-windows_open(const char *name, char *suffix, struct stream **streamp,
- uint8_t dscp)
-{
- int error, port;
- FILE *file;
- char *suffix_new, *path;
-
- /* If the path does not contain a ':', assume it is relative to
- * OVS_RUNDIR. */
- if (!strchr(suffix, ':')) {
- path = xasprintf("%s/%s", ovs_rundir(), suffix);
- } else {
- path = xstrdup(suffix);
- }
-
- file = fopen(path, "r");
- if (!file) {
- error = errno;
- VLOG_DBG("%s: could not open %s (%s)", name, suffix,
- ovs_strerror(error));
- return error;
- }
-
- error = fscanf(file, "%d", &port);
- if (error != 1) {
- VLOG_ERR("failed to read port from %s", suffix);
- fclose(file);
- return EINVAL;
- }
- fclose(file);
-
- suffix_new = xasprintf("127.0.0.1:%d", port);
-
- error = tcp_open(name, suffix_new, streamp, dscp);
-
- free(suffix_new);
- free(path);
- return error;
-}
-
-const struct stream_class windows_stream_class = {
- "unix", /* name */
- false, /* needs_probes */
- windows_open, /* open */
- NULL, /* close */
- NULL, /* connect */
- NULL, /* recv */
- NULL, /* send */
- NULL, /* run */
- NULL, /* run_wait */
- NULL, /* wait */
-};
-#endif
/* Passive TCP. */
@@ -198,60 +140,3 @@ const struct pstream_class ptcp_pstream_class = {
NULL,
NULL,
};
-
-#ifdef _WIN32
-static int
-pwindows_open(const char *name, char *suffix, struct pstream **pstreamp,
- uint8_t dscp)
-{
- int error;
- char *suffix_new, *path;
- FILE *file;
- struct pstream *listener;
-
- suffix_new = xstrdup("0:127.0.0.1");
-
- /* If the path does not contain a ':', assume it is relative to
- * OVS_RUNDIR. */
- if (!strchr(suffix, ':')) {
- path = xasprintf("%s/%s", ovs_rundir(), suffix);
- } else {
- path = xstrdup(suffix);
- }
-
- error = new_pstream(suffix_new, name, pstreamp, dscp, path, false);
- if (error) {
- goto exit;
- }
- listener = *pstreamp;
-
- file = fopen(path, "w");
- if (!file) {
- error = errno;
- VLOG_DBG("could not open %s (%s)", path, ovs_strerror(error));
- goto exit;
- }
-
- fprintf(file, "%d\n", ntohs(listener->bound_port));
- if (fflush(file) == EOF) {
- error = EIO;
- VLOG_ERR("write failed for %s", path);
- fclose(file);
- goto exit;
- }
- fclose(file);
-
-exit:
- free(suffix_new);
- return error;
-}
-
-const struct pstream_class pwindows_pstream_class = {
- "punix",
- false,
- pwindows_open,
- NULL,
- NULL,
- NULL,
-};
-#endif
diff --git a/lib/stream-windows.c b/lib/stream-windows.c
new file mode 100644
index 000000000..e0fe012fb
--- /dev/null
+++ b/lib/stream-windows.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 2016 Cloudbase Solutions Srl
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "poll-loop.h"
+#include "dirs.h"
+#include "util.h"
+#include "stream-provider.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(stream_windows);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
+
+static void maybe_unlink_and_free(char *path);
+
+/* Suggested buffer size at the creation of the named pipe for reading and
+ * and writing operations. */
+#define BUFSIZE 65000
+
+/* Default prefix of a local named pipe. */
+#define LOCAL_PREFIX "\\\\.\\pipe\\"
+
+/* This function has the purpose to remove all the slashes received in s. */
+static char *
+remove_slashes(char *s)
+{
+ char *p1, *p2;
+ p1 = p2 = s;
+
+ while (*p1) {
+ if ((*p1) == '\\' || (*p1) == '/') {
+ p1++;
+ } else {
+ *p2 = *p1;
+ p2++;
+ p1++;
+ }
+ }
+ *p2 = '\0';
+ return s;
+}
+
+/* Active named pipe. */
+struct windows_stream
+{
+ struct stream stream;
+ HANDLE fd;
+ /* Overlapped operations used for reading/writing. */
+ OVERLAPPED read;
+ OVERLAPPED write;
+ /* Flag to check if a reading/writing operation is pending. */
+ bool read_pending;
+ bool write_pending;
+ /* Flag to check if fd is a server HANDLE. In the case of a server handle
+ * we have to issue a disconnect before closing the actual handle. */
+ bool server;
+ bool retry_connect;
+ char *pipe_path;
+};
+
+static struct windows_stream *
+stream_windows_cast(struct stream *stream)
+{
+ stream_assert_class(stream, &windows_stream_class);
+ return CONTAINER_OF(stream, struct windows_stream, stream);
+}
+
+static HANDLE
+create_snpipe(char *path)
+{
+ return CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED |
+ FILE_FLAG_NO_BUFFERING,
+ NULL);
+}
+
+/* Active named pipe open. */
+static int
+windows_open(const char *name, char *suffix, struct stream **streamp,
+ uint8_t dscp OVS_UNUSED)
+{
+ char *connect_path;
+ HANDLE npipe;
+ DWORD mode = PIPE_READMODE_BYTE;
+ char *path;
+ FILE *file;
+ bool retry = false;
+ /* If the path does not contain a ':', assume it is relative to
+ * OVS_RUNDIR. */
+ if (!strchr(suffix, ':')) {
+ path = xasprintf("%s/%s", ovs_rundir(), suffix);
+ } else {
+ path = xstrdup(suffix);
+ }
+
+ /* In case of "unix:" argument, the assumption is that there is a file
+ * created in the path (name). */
+ file = fopen(path, "r");
+ if (!file) {
+ free(path);
+ VLOG_DBG_RL(&rl, "%s: could not open %s (%s)", name, suffix,
+ ovs_strerror(errno));
+ return ENOENT;
+ } else {
+ fclose(file);
+ }
+
+ /* Valid pipe names do not have slashes. The assumption is that the named
+ * pipe was created with the name "path", with slashes removed and the
+ * default prefix \\.\pipe\ appended.
+ * Strip the slashes from the parameter name and append the default prefix.
+ */
+ connect_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
+ free(path);
+
+ /* Try to connect to the named pipe. In case all pipe instances are
+ * busy we set the retry flag to true and retry again during the
+ * connect function. Use overlapped flag and file no buffering to ensure
+ * asynchronous operations. */
+ npipe = create_snpipe(connect_path);
+
+ if (npipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY) {
+ retry = true;
+ }
+
+ if (!retry && npipe == INVALID_HANDLE_VALUE) {
+ VLOG_ERR_RL(&rl, "Could not connect to named pipe: %s",
+ ovs_lasterror_to_string());
+ free(connect_path);
+ return ENOENT;
+ }
+ if (!retry && !SetNamedPipeHandleState(npipe, &mode, NULL, NULL)) {
+ VLOG_ERR_RL(&rl, "Could not set named pipe options: %s",
+ ovs_lasterror_to_string());
+ free(connect_path);
+ CloseHandle(npipe);
+ return ENOENT;
+ }
+ struct windows_stream *s = xmalloc(sizeof *s);
+ stream_init(&s->stream, &windows_stream_class, 0, name);
+ s->pipe_path = connect_path;
+ s->fd = npipe;
+ /* This is an active stream. */
+ s->server = false;
+ /* Create events for reading and writing to be signaled later. */
+ memset(&s->read, 0, sizeof(s->read));
+ memset(&s->write, 0, sizeof(s->write));
+ s->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ s->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ /* Initial read and write operations are not pending. */
+ s->read_pending = false;
+ s->write_pending = false;
+ s->retry_connect = retry;
+ *streamp = &s->stream;
+ return 0;
+}
+
+/* Active named pipe close. */
+static void
+windows_close(struct stream *stream)
+{
+ struct windows_stream *s = stream_windows_cast(stream);
+ /* Disconnect the named pipe in case it was created from a passive stream.
+ */
+ if (s->server) {
+ DisconnectNamedPipe(s->fd);
+ }
+ CloseHandle(s->fd);
+ CloseHandle(s->read.hEvent);
+ CloseHandle(s->write.hEvent);
+ if (s->pipe_path) {
+ free(s->pipe_path);
+ }
+ free(s);
+}
+
+/* Active named pipe connect. */
+static int
+windows_connect(struct stream *stream)
+{
+ struct windows_stream *s = stream_windows_cast(stream);
+
+ if (!s->retry_connect) {
+ return 0;
+ } else {
+ HANDLE npipe;
+ npipe = create_snpipe(s->pipe_path);
+ if (npipe == INVALID_HANDLE_VALUE) {
+ if (GetLastError() == ERROR_PIPE_BUSY) {
+ return EAGAIN;
+ } else {
+ s->retry_connect = false;
+ return ENOENT;
+ }
+ }
+ s->retry_connect = false;
+ s->fd = npipe;
+ return 0;
+ }
+}
+
+/* Active named pipe receive. */
+static ssize_t
+windows_recv(struct stream *stream, void *buffer, size_t n)
+{
+ struct windows_stream *s = stream_windows_cast(stream);
+ ssize_t retval = 0;
+ boolean result = false;
+ DWORD last_error = 0;
+ LPOVERLAPPED ov = NULL;
+ ov = &s->read;
+
+ /* If the read operation was pending, we verify its result. */
+ if (s->read_pending) {
+ if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
+ last_error = GetLastError();
+ if (last_error == ERROR_IO_INCOMPLETE) {
+ /* If the operation is still pending, retry again. */
+ s->read_pending = true;
+ return -EAGAIN;
+ } else if (last_error == ERROR_PIPE_NOT_CONNECTED
+ || last_error == ERROR_BAD_PIPE
+ || last_error == ERROR_NO_DATA
+ || last_error == ERROR_BROKEN_PIPE) {
+ /* If the pipe was disconnected, return 0. */
+ return 0;
+ } else {
+ VLOG_ERR_RL(&rl, "Could not receive data on named pipe. Last "
+ "error: %s", ovs_lasterror_to_string());
+ return -EINVAL;
+ }
+ }
+ s->read_pending = false;
+ return retval;
+ }
+
+ result = ReadFile(s->fd, buffer, n, &(DWORD)retval, ov);
+
+ if (!result && GetLastError() == ERROR_IO_PENDING) {
+ /* Mark the read operation as pending. */
+ s->read_pending = true;
+ return -EAGAIN;
+ } else if (!result) {
+ last_error = GetLastError();
+ if (last_error == ERROR_PIPE_NOT_CONNECTED
+ || last_error == ERROR_BAD_PIPE
+ || last_error == ERROR_NO_DATA
+ || last_error == ERROR_BROKEN_PIPE) {
+ /* If the pipe was disconnected, return 0. */
+ return 0;
+ }
+ VLOG_ERR_RL(&rl, "Could not receive data synchronous on named pipe."
+ "Last error: %s", ovs_lasterror_to_string());
+ return -EINVAL;
+ }
+
+ return retval;
+}
+
+/* Active named pipe send. */
+static ssize_t
+windows_send(struct stream *stream, const void *buffer, size_t n)
+{
+ struct windows_stream *s = stream_windows_cast(stream);
+ ssize_t retval = 0;
+ boolean result = false;
+ DWORD last_error = 0;
+ LPOVERLAPPED ov = NULL;
+ ov = &s->write;
+
+ /* If the send operation was pending, we verify the result. */
+ if (s->write_pending) {
+ if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
+ last_error = GetLastError();
+ if (last_error == ERROR_IO_INCOMPLETE) {
+ /* If the operation is still pending, retry again. */
+ s->write_pending = true;
+ return -EAGAIN;
+ } else if (last_error == ERROR_PIPE_NOT_CONNECTED
+ || last_error == ERROR_BAD_PIPE
+ || last_error == ERROR_NO_DATA
+ || last_error == ERROR_BROKEN_PIPE) {
+ /* If the pipe was disconnected, return connection reset. */
+ return -WSAECONNRESET;
+ } else {
+ VLOG_ERR_RL(&rl, "Could not send data on named pipe. Last "
+ "error: %s", ovs_lasterror_to_string());
+ return -EINVAL;
+ }
+ }
+ s->write_pending = false;
+ return retval;
+ }
+
+ result = WriteFile(s->fd, buffer, n, &(DWORD)retval, ov);
+ last_error = GetLastError();
+ if (!result && GetLastError() == ERROR_IO_PENDING) {
+ /* Mark the send operation as pending. */
+ s->write_pending = true;
+ return -EAGAIN;
+ } else if (!result && (last_error == ERROR_PIPE_NOT_CONNECTED
+ || last_error == ERROR_BAD_PIPE
+ || last_error == ERROR_NO_DATA
+ || last_error == ERROR_BROKEN_PIPE)) {
+ /* If the pipe was disconnected, return connection reset. */
+ return -WSAECONNRESET;
+ } else if (!result) {
+ VLOG_ERR_RL(&rl, "Could not send data on synchronous named pipe. Last "
+ "error: %s", ovs_lasterror_to_string());
+ return -EINVAL;
+ }
+ return (retval > 0 ? retval : -EAGAIN);
+}
+
+/* Active named pipe wait. */
+static void
+windows_wait(struct stream *stream, enum stream_wait_type wait)
+{
+ struct windows_stream *s = stream_windows_cast(stream);
+ switch (wait) {
+ case STREAM_SEND:
+ poll_wevent_wait(s->write.hEvent);
+ break;
+
+ case STREAM_CONNECT:
+ poll_immediate_wake();
+ break;
+
+ case STREAM_RECV:
+ poll_wevent_wait(s->read.hEvent);
+ break;
+
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+/* Passive named pipe. */
+const struct stream_class windows_stream_class = {
+ "unix", /* name */
+ false, /* needs_probes */
+ windows_open, /* open */
+ windows_close, /* close */
+ windows_connect, /* connect */
+ windows_recv, /* recv */
+ windows_send, /* send */
+ NULL, /* run */
+ NULL, /* run_wait */
+ windows_wait, /* wait */
+};
+
+struct pwindows_pstream
+{
+ struct pstream pstream;
+ HANDLE fd;
+ /* Unlink path to be deleted during close. */
+ char *unlink_path;
+ /* Overlapped operation used for connect. */
+ OVERLAPPED connect;
+ /* Flag to check if an operation is pending. */
+ bool pending;
+ /* String used to create the named pipe. */
+ char *pipe_path;
+};
+
+const struct pstream_class pwindows_pstream_class;
+
+static struct pwindows_pstream *
+pwindows_pstream_cast(struct pstream *pstream)
+{
+ pstream_assert_class(pstream, &pwindows_pstream_class);
+ return CONTAINER_OF(pstream, struct pwindows_pstream, pstream);
+}
+
+/* Create a named pipe with read/write access, overlapped, message mode for
+ * writing, byte mode for reading and with a maximum of 64 active instances. */
+static HANDLE
+create_pnpipe(char *name)
+{
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+ if (strlen(name) > 256) {
+ VLOG_ERR_RL(&rl, "Named pipe name too long.");
+ return INVALID_HANDLE_VALUE;
+ }
+ return CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE | PIPE_WAIT,
+ 64, BUFSIZE, BUFSIZE, 0, &sa);
+}
+
+/* Passive named pipe connect. This function creates a new named pipe and
+ * passes the old handle to the active stream. */
+static int
+pwindows_accept(struct pstream *pstream, struct stream **new_streamp)
+{
+ struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
+ DWORD last_error = 0;
+ DWORD cbRet;
+ HANDLE npipe;
+
+ /* If the connect operation was pending, verify the result. */
+ if (p->pending) {
+ if (!GetOverlappedResult(p->fd, &p->connect, &cbRet, FALSE)) {
+ last_error = GetLastError();
+ if (last_error == ERROR_IO_INCOMPLETE) {
+ /* If the operation is still pending, retry again. */
+ p->pending = true;
+ return EAGAIN;
+ } else {
+ VLOG_ERR_RL(&rl, "Could not connect named pipe. Last "
+ "error: %s", ovs_lasterror_to_string());
+ return EINVAL;
+ }
+ }
+ p->pending = false;
+ }
+
+ if (!p->pending && !ConnectNamedPipe(p->fd, &p->connect)) {
+ last_error = GetLastError();
+ if (last_error == ERROR_IO_PENDING) {
+ /* Mark the accept operation as pending. */
+ p->pending = true;
+ return EAGAIN;
+ } else if (last_error != ERROR_PIPE_CONNECTED) {
+ VLOG_ERR_RL(&rl, "Could not connect synchronous named pipe. Last "
+ "error: %s", ovs_lasterror_to_string());
+ return EINVAL;
+ } else {
+ /* If the pipe is connected, signal an event. */
+ SetEvent(&p->connect.hEvent);
+ }
+ }
+
+ npipe = create_pnpipe(p->pipe_path);
+ if (npipe == INVALID_HANDLE_VALUE) {
+ VLOG_ERR_RL(&rl, "Could not create a new named pipe after connect. ",
+ ovs_lasterror_to_string());
+ return ENOENT;
+ }
+
+ /* Give the handle p->fd to the new created active stream and specify it
+ * was created by an active stream. */
+ struct windows_stream *p_temp = xmalloc(sizeof *p_temp);
+ stream_init(&p_temp->stream, &windows_stream_class, 0, "unix");
+ p_temp->fd = p->fd;
+ /* Specify it was created by a passive stream. */
+ p_temp->server = true;
+ /* Create events for read/write operations. */
+ memset(&p_temp->read, 0, sizeof(p_temp->read));
+ memset(&p_temp->write, 0, sizeof(p_temp->write));
+ p_temp->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ p_temp->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ p_temp->read_pending = false;
+ p_temp->write_pending = false;
+ p_temp->retry_connect = false;
+ p_temp->pipe_path = NULL;
+ *new_streamp = &p_temp->stream;
+
+ /* The passive handle p->fd will be the new created handle. */
+ p->fd = npipe;
+ memset(&p->connect, 0, sizeof(p->connect));
+ p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ p->pending = false;
+ return 0;
+}
+
+/* Passive named pipe close. */
+static void
+pwindows_close(struct pstream *pstream)
+{
+ struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
+ DisconnectNamedPipe(p->fd);
+ CloseHandle(p->fd);
+ CloseHandle(p->connect.hEvent);
+ maybe_unlink_and_free(p->unlink_path);
+ free(p->pipe_path);
+ free(p);
+}
+
+/* Passive named pipe wait. */
+static void
+pwindows_wait(struct pstream *pstream)
+{
+ struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
+ poll_wevent_wait(p->connect.hEvent);
+}
+
+/* Passive named pipe. */
+static int
+pwindows_open(const char *name OVS_UNUSED, char *suffix,
+ struct pstream **pstreamp, uint8_t dscp OVS_UNUSED)
+{
+ char *bind_path;
+ int error;
+ HANDLE npipe;
+ char *orig_path;
+
+ char *path;
+ if (!strchr(suffix, ':')) {
+ path = xasprintf("%s/%s", ovs_rundir(), suffix);
+ } else {
+ path = xstrdup(suffix);
+ }
+
+ /* Try to create a file under the path location. */
+ FILE *file = fopen(path, "w");
+ if (!file) {
+ free(path);
+ error = errno;
+ VLOG_DBG_RL(&rl, "could not open %s (%s)", path, ovs_strerror(error));
+ return error;
+ } else {
+ fclose(file);
+ }
+
+ orig_path = xstrdup(path);
+ /* Strip slashes from path and create a named pipe using that newly created
+ * string. */
+ bind_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
+ free(path);
+
+ npipe = create_pnpipe(bind_path);
+
+ if (npipe == INVALID_HANDLE_VALUE) {
+ VLOG_ERR_RL(&rl, "Could not create named pipe. Last error: %s",
+ ovs_lasterror_to_string());
+ return ENOENT;
+ }
+
+ struct pwindows_pstream *p = xmalloc(sizeof *p);
+ pstream_init(&p->pstream, &pwindows_pstream_class, name);
+ p->fd = npipe;
+ p->unlink_path = orig_path;
+ memset(&p->connect, 0, sizeof(p->connect));
+ p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ p->pending = false;
+ p->pipe_path = bind_path;
+ *pstreamp = &p->pstream;
+ return 0;
+}
+
+const struct pstream_class pwindows_pstream_class = {
+ "punix",
+ false, /* probes */
+ pwindows_open, /* open */
+ pwindows_close, /* close */
+ pwindows_accept, /* accept */
+ pwindows_wait, /* wait */
+};
+
+/* Helper functions. */
+static void
+maybe_unlink_and_free(char *path)
+{
+ if (path) {
+ fatal_signal_unlink_file_now(path);
+ free(path);
+ }
+}
diff --git a/lib/unixctl.c b/lib/unixctl.c
index 5e5d26c68..57d6577b2 100644
--- a/lib/unixctl.c
+++ b/lib/unixctl.c
@@ -195,7 +195,7 @@ unixctl_command_reply_error(struct unixctl_conn *conn, const char *error)
* - An absolute path (starting with '/') that gives the exact name of
* the Unix domain socket to listen on.
*
- * For Windows, a kernel assigned TCP port is used and written in 'path'
+ * For Windows, a local named pipe is used. A file is created in 'path'
* which may be:
*
* - NULL, in which case <rundir>/<program>.ctl is used.
@@ -442,7 +442,8 @@ unixctl_server_destroy(struct unixctl_server *server)
* be the name of a unixctl server socket. If it does not start with '/', it
* will be prefixed with the rundir (e.g. /usr/local/var/run/openvswitch).
*
- * On Windows, connects to a localhost TCP port as written inside 'path'.
+ * On Windows, connects to a local named pipe. A file which resides in
+ * 'path' is used to mimic the behavior of a Unix domain socket.
* 'path' should be an absolute path of the file.
*
* Returns 0 if successful, otherwise a positive errno value. If successful,
diff --git a/lib/unixctl.man b/lib/unixctl.man
index b681c7d2e..f72eae8d7 100644
--- a/lib/unixctl.man
+++ b/lib/unixctl.man
@@ -7,11 +7,11 @@ not used at all, the default socket is
\fB@RUNDIR@/\*(PN.\fIpid\fB.ctl\fR, where \fIpid\fR is \fB\*(PN\fR's
process ID.
.IP
-On Windows, uses a kernel chosen TCP port on the localhost to listen
-for runtime management commands. The kernel chosen TCP port value is written
-in a file whose absolute path is pointed by \fIsocket\fR. If \fB\-\-unixctl\fR
-is not used at all, the file is created as \fB\*(PN.ctl\fR in the configured
-\fIOVS_RUNDIR\fR directory.
+On Windows a local named pipe is used to listen for runtime management
+commands. A file is created in the absolute path as pointed by
+\fIsocket\fR or if \fB\-\-unixctl\fR is not used at all, a file is
+created as \fB\*(PN.ctl\fR in the configured \fIOVS_RUNDIR\fR
+directory. The file exists just to mimic the behavior of a Unix domain socket.
.IP
Specifying \fBnone\fR for \fIsocket\fR disables the control socket
feature.
diff --git a/lib/vconn-active.man b/lib/vconn-active.man
index 252438da7..3e789cc88 100644
--- a/lib/vconn-active.man
+++ b/lib/vconn-active.man
@@ -11,4 +11,6 @@ If \fIport\fR is not specified, it defaults to 6653.
\fBunix:\fIfile\fR
On POSIX, a Unix domain server socket named \fIfile\fR.
.IP
-On Windows, a localhost TCP port written in \fIfile\fR.
+On Windows, connect to a local named pipe that is represented by a
+file created in the path \fIfile\fR to mimic the behavior of a Unix
+domain socket.
diff --git a/ovsdb/remote-active.man b/ovsdb/remote-active.man
index 22b350c1a..83d64652d 100644
--- a/ovsdb/remote-active.man
+++ b/ovsdb/remote-active.man
@@ -14,5 +14,6 @@ square brackets, e.g.: \fBtcp:[::1]:6640\fR.
.IP "\fBunix:\fIfile\fR"
On POSIX, connect to the Unix domain server socket named \fIfile\fR.
.IP
-On Windows, connect to a localhost TCP port whose value is written in
-\fIfile\fR.
+On Windows, connect to a local named pipe that is represented by a file
+created in the path \fIfile\fR to mimic the behavior of a Unix domain
+socket.
diff --git a/ovsdb/remote-passive.man b/ovsdb/remote-passive.man
index a05f79695..5da2de87b 100644
--- a/ovsdb/remote-passive.man
+++ b/ovsdb/remote-passive.man
@@ -22,5 +22,5 @@ an IPv6 address, then wrap \fIip\fR with square brackets, e.g.:
On POSIX, listen on the Unix domain server socket named \fIfile\fR for a
connection.
.IP
-On Windows, listen on a kernel chosen TCP port on the localhost. The kernel
-chosen TCP port value is written in \fIfile\fR.
+On Windows, listen on a local named pipe. A file is created in the
+path \fIfile\fR to mimic the behavior of a Unix domain socket.
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
index e70498dfd..d9d24690b 100644
--- a/tests/ovsdb-server.at
+++ b/tests/ovsdb-server.at
@@ -388,7 +388,7 @@ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile db])
AT_CHECK([test ! -e socket1])
AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote punix:socket1])
if test "$IS_WIN32" = "yes"; then
- OVS_WAIT_UNTIL([test -s socket1])
+ OVS_WAIT_UNTIL([test -e socket1])
else
OVS_WAIT_UNTIL([test -S socket1])
fi
@@ -399,7 +399,7 @@ AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes],
AT_CHECK([test ! -e socket2])
AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote punix:socket2])
if test "$IS_WIN32" = "yes"; then
- OVS_WAIT_UNTIL([test -s socket2])
+ OVS_WAIT_UNTIL([test -e socket2])
else
OVS_WAIT_UNTIL([test -S socket2])
fi
@@ -416,7 +416,7 @@ ovs-appctl: ovsdb-server: server returned an error
AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-remote punix:socket1])
OVS_WAIT_UNTIL([test ! -e socket1])
if test "$IS_WIN32" = "yes"; then
- AT_CHECK([test -s socket2])
+ AT_CHECK([test -e socket2])
else
AT_CHECK([test -S socket2])
fi