Skip to content

Commit

Permalink
unixctl: Add support for Windows.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Ben Pfaff <[email protected]>
  • Loading branch information
shettyg committed Mar 28, 2014
1 parent 508caab commit cb54a8c
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 11 deletions.
90 changes: 79 additions & 11 deletions lib/unixctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "poll-loop.h"
#include "shash.h"
#include "stream.h"
#include "stream-provider.h"
#include "svec.h"
#include "vlog.h"

Expand Down Expand Up @@ -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 <rundir>/<program>.<pid>.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
* <rundir>.
*
* - 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 <rundir>/<program>.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,
Expand All @@ -203,29 +211,59 @@ 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) {
ovs_error(error, "could not initialize control socket %s", punix_path);
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);

Expand All @@ -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;
}
Expand Down Expand Up @@ -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. */
Expand All @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions lib/unixctl.man
Original file line number Diff line number Diff line change
Expand Up @@ -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.

0 comments on commit cb54a8c

Please sign in to comment.