From cb54a8c57646a1549dcff0c2ad4c2d8b46bc2880 Mon Sep 17 00:00:00 2001 From: Gurucharan Shetty Date: Fri, 28 Mar 2014 08:19:59 -0700 Subject: [PATCH] unixctl: Add support for Windows. For Windows, use a kernel assigned localhost TCP port to listen for runtime management connections and then write it into a file so that a client can read it and then make a TCP connection. Since we do not have the infrastructure to create pidfiles on windows as of now, we create the *.ctl file without a pid. This should be okay since we use different OVS_RUNDIR when we run multiple copies of a daemon. We do not generate man pages on Windows. But we still update them for Windows so that anyone can read it elsewhere. Since we do not generate it directly, we cannot dynamically show the configured OVS_RUNDIR in windows. So, I have a not so nice \fIOVS_RUNDIR\fR in the man page. Signed-off-by: Gurucharan Shetty Acked-by: Ben Pfaff --- lib/unixctl.c | 90 +++++++++++++++++++++++++++++++++++++++++++------ lib/unixctl.man | 7 ++++ 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/lib/unixctl.c b/lib/unixctl.c index 253e95389..20acc3c9c 100644 --- a/lib/unixctl.c +++ b/lib/unixctl.c @@ -27,6 +27,7 @@ #include "poll-loop.h" #include "shash.h" #include "stream.h" +#include "stream-provider.h" #include "svec.h" #include "vlog.h" @@ -177,19 +178,26 @@ unixctl_command_reply_error(struct unixctl_conn *conn, const char *error) unixctl_command_reply__(conn, false, error); } -/* Creates a unixctl server listening on 'path', which may be: +/* Creates a unixctl server listening on 'path', which for POSIX may be: * * - NULL, in which case /..ctl is used. * - * - "none", in which case the function will return successfully but - * no socket will actually be created. - * * - A name that does not start with '/', in which case it is put in * . * * - 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' + * which may be: + * + * - NULL, in which case /.ctl is used. + * + * - An absolute path that gives the name of the file. + * + * For both POSIX and Windows, if the path is "none", the function will + * return successfully but no socket will actually be created. + * * A program that (optionally) daemonizes itself should call this function * *after* daemonization, so that the socket name contains the pid of the * daemon instead of the pid of the program that exited. (Otherwise, @@ -203,22 +211,28 @@ unixctl_server_create(const char *path, struct unixctl_server **serverp) { struct unixctl_server *server; struct pstream *listener; - char *punix_path; + char *punix_path, *abs_path = NULL; int error; +#ifdef _WIN32 + FILE *file; +#endif *serverp = NULL; if (path && !strcmp(path, "none")) { return 0; } +#ifndef _WIN32 if (path) { - char *abs_path = abs_file_name(ovs_rundir(), path); + abs_path = abs_file_name(ovs_rundir(), path); punix_path = xasprintf("punix:%s", abs_path); - free(abs_path); } else { punix_path = xasprintf("punix:%s/%s.%ld.ctl", ovs_rundir(), program_name, (long int) getpid()); } +#else + punix_path = xstrdup("ptcp:0:127.0.0.1"); +#endif error = pstream_open(punix_path, &listener, 0); if (error) { @@ -226,6 +240,30 @@ unixctl_server_create(const char *path, struct unixctl_server **serverp) goto exit; } +#ifdef _WIN32 + if (path) { + abs_path = xstrdup(path); + } else { + abs_path = xasprintf("%s/%s.ctl", ovs_rundir(), program_name); + } + + file = fopen(abs_path, "w"); + if (!file) { + error = errno; + ovs_error(error, "could not open %s", abs_path); + goto exit; + } + + fprintf(file, "%d\n", ntohs(listener->bound_port)); + if (fflush(file) == EOF) { + error = EIO; + ovs_error(error, "write failed for %s", abs_path); + fclose(file); + goto exit; + } + fclose(file); +#endif + unixctl_command_register("help", "", 0, 0, unixctl_help, NULL); unixctl_command_register("version", "", 0, 0, unixctl_version, NULL); @@ -235,6 +273,9 @@ unixctl_server_create(const char *path, struct unixctl_server **serverp) *serverp = server; exit: + if (abs_path) { + free(abs_path); + } free(punix_path); return error; } @@ -404,9 +445,12 @@ unixctl_server_destroy(struct unixctl_server *server) } } -/* Connects to a unixctl server socket. 'path' should 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 POSIX based systems, connects to a unixctl server socket. 'path' should + * 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'. + * 'path' should be an absolute path of the file. * * Returns 0 if successful, otherwise a positive errno value. If successful, * sets '*client' to the new jsonrpc, otherwise to NULL. */ @@ -416,11 +460,35 @@ unixctl_client_create(const char *path, struct jsonrpc **client) char *abs_path, *unix_path; struct stream *stream; int error; +#ifdef _WIN32 + FILE *file; + int port; + + abs_path = strdup(path); + file = fopen(abs_path, "r"); + if (!file) { + int error = errno; + ovs_error(error, "could not open %s", abs_path); + free(abs_path); + return error; + } - *client = NULL; + error = fscanf(file, "%d", &port); + if (error != 1) { + ovs_error(errno, "failed to read port from %s", abs_path); + free(abs_path); + return EINVAL; + } + fclose(file); + unix_path = xasprintf("tcp:127.0.0.1:%d", port); +#else abs_path = abs_file_name(ovs_rundir(), path); unix_path = xasprintf("unix:%s", abs_path); +#endif + + *client = NULL; + error = stream_open_block(stream_open(unix_path, &stream, DSCP_DEFAULT), &stream); free(unix_path); diff --git a/lib/unixctl.man b/lib/unixctl.man index a15408666..b681c7d2e 100644 --- a/lib/unixctl.man +++ b/lib/unixctl.man @@ -6,5 +6,12 @@ interpreted as relative to \fB@RUNDIR@\fR. If \fB\-\-unixctl\fR is 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. +.IP Specifying \fBnone\fR for \fIsocket\fR disables the control socket feature. -- 2.43.0