X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Funixctl.c;h=caaf252dff7213623a782de3ed23d7521ccca5cb;hb=524e1b1c3dbf4f88c00c4b6c2544a334d48dab0a;hp=164a7db6e6b3fe601fe5efc12dff1e74505c6d80;hpb=3fbe1d307e27ed99528d83cf866c7df497e58b77;p=sliver-openvswitch.git diff --git a/lib/unixctl.c b/lib/unixctl.c index 164a7db6e..caaf252df 100644 --- a/lib/unixctl.c +++ b/lib/unixctl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,17 +34,24 @@ #include "poll-loop.h" #include "shash.h" #include "socket-util.h" +#include "svec.h" #include "util.h" +#include "vlog.h" #ifndef SCM_CREDENTIALS #include #endif -#define THIS_MODULE VLM_unixctl -#include "vlog.h" +VLOG_DEFINE_THIS_MODULE(unixctl); + +COVERAGE_DEFINE(unixctl_received); +COVERAGE_DEFINE(unixctl_replied); struct unixctl_command { - void (*cb)(struct unixctl_conn *, const char *args); + const char *usage; + int min_args, max_args; + unixctl_cb_func *cb; + void *aux; }; struct unixctl_conn { @@ -76,29 +83,65 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); static struct shash commands = SHASH_INITIALIZER(&commands); static void -unixctl_help(struct unixctl_conn *conn, const char *args UNUSED) +unixctl_help(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; - struct shash_node *node; + const struct shash_node **nodes = shash_sort(&commands); + size_t i; ds_put_cstr(&ds, "The available commands are:\n"); - SHASH_FOR_EACH (node, &commands) { - ds_put_format(&ds, "\t%s\n", node->name); + + for (i = 0; i < shash_count(&commands); i++) { + const struct shash_node *node = nodes[i]; + const struct unixctl_command *command = node->data; + + ds_put_format(&ds, " %-23s %s\n", node->name, command->usage); } + free(nodes); + unixctl_command_reply(conn, 214, ds_cstr(&ds)); ds_destroy(&ds); } +static void +unixctl_version(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) +{ + unixctl_command_reply(conn, 200, get_program_version()); +} + +/* Registers a unixctl command with the given 'name'. 'usage' describes the + * arguments to the command; it is used only for presentation to the user in + * "help" output. + * + * 'cb' is called when the command is received. It is passed the actual set of + * arguments, as a text string, plus a copy of 'aux'. Normally 'cb' should + * call unixctl_command_reply() before it returns, but if the command cannot be + * handled immediately then it can defer the reply until later. A given + * connection can only process a single request at a time, so + * unixctl_command_reply() must be called eventually to avoid blocking that + * connection. */ void -unixctl_command_register(const char *name, - void (*cb)(struct unixctl_conn *, const char *args)) +unixctl_command_register(const char *name, const char *usage, + int min_args, int max_args, + unixctl_cb_func *cb, void *aux) { struct unixctl_command *command; + struct unixctl_command *lookup = shash_find_data(&commands, name); + + assert(!lookup || lookup->cb == cb); + + if (lookup) { + return; + } - assert(!shash_find_data(&commands, name) - || shash_find_data(&commands, name) == cb); command = xmalloc(sizeof *command); + command->usage = usage; + command->min_args = min_args; + command->max_args = max_args; command->cb = cb; + command->aux = aux; shash_add(&commands, name, command); } @@ -159,6 +202,9 @@ unixctl_command_reply(struct unixctl_conn *conn, * * - 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 * . * @@ -171,26 +217,29 @@ unixctl_command_reply(struct unixctl_conn *conn, * "ovs-appctl --target=" will fail.) * * Returns 0 if successful, otherwise a positive errno value. If successful, - * sets '*serverp' to the new unixctl_server, otherwise to NULL. */ + * sets '*serverp' to the new unixctl_server (or to NULL if 'path' was "none"), + * otherwise to NULL. */ int unixctl_server_create(const char *path, struct unixctl_server **serverp) { struct unixctl_server *server; int error; - unixctl_command_register("help", unixctl_help); + if (path && !strcmp(path, "none")) { + *serverp = NULL; + return 0; + } + + unixctl_command_register("help", "", 0, 0, unixctl_help, NULL); + unixctl_command_register("version", "", 0, 0, unixctl_version, NULL); server = xmalloc(sizeof *server); list_init(&server->conns); if (path) { - if (path[0] == '/') { - server->path = xstrdup(path); - } else { - server->path = xasprintf("%s/%s", ovs_rundir, path); - } + server->path = abs_file_name(ovs_rundir(), path); } else { - server->path = xasprintf("%s/%s.%ld.ctl", ovs_rundir, + server->path = xasprintf("%s/%s.%ld.ctl", ovs_rundir(), program_name, (long int) getpid()); } @@ -198,22 +247,21 @@ unixctl_server_create(const char *path, struct unixctl_server **serverp) NULL); if (server->fd < 0) { error = -server->fd; - fprintf(stderr, "Could not initialize control socket %s (%s)\n", - server->path, strerror(error)); + ovs_error(error, "could not initialize control socket %s", + server->path); goto error; } if (chmod(server->path, S_IRUSR | S_IWUSR) < 0) { error = errno; - fprintf(stderr, "Failed to chmod control socket %s (%s)\n", - server->path, strerror(error)); + ovs_error(error, "failed to chmod control socket %s", server->path); goto error; } if (listen(server->fd, 10) < 0) { error = errno; - fprintf(stderr, "Failed to listen on control socket %s (%s)\n", - server->path, strerror(error)); + ovs_error(error, "Failed to listen on control socket %s", + server->path); goto error; } @@ -268,26 +316,43 @@ static void process_command(struct unixctl_conn *conn, char *s) { struct unixctl_command *command; - size_t name_len; - char *name, *args; + struct svec argv; COVERAGE_INC(unixctl_received); conn->state = S_PROCESS; - name = s; - name_len = strcspn(name, " "); - args = name + name_len; - args += strspn(args, " "); - name[name_len] = '\0'; + svec_init(&argv); + svec_parse_words(&argv, s); + svec_terminate(&argv); - command = shash_find_data(&commands, name); - if (command) { - command->cb(conn, args); + if (argv.n == 0) { + unixctl_command_reply(conn, 400, "missing command name in request"); } else { - char *msg = xasprintf("\"%s\" is not a valid command", name); - unixctl_command_reply(conn, 400, msg); - free(msg); + const char *name = argv.names[0]; + char *error; + + command = shash_find_data(&commands, name); + if (!command) { + error = xasprintf("\"%s\" is not a valid command", name); + } else if (argv.n - 1 < command->min_args) { + error = xasprintf("\"%s\" command requires at least %d arguments", + name, command->min_args); + } else if (argv.n - 1 > command->max_args) { + error = xasprintf("\"%s\" command takes at most %d arguments", + name, command->max_args); + } else { + error = NULL; + command->cb(conn, argv.n, (const char **) argv.names, + command->aux); + } + + if (error) { + unixctl_command_reply(conn, 400, error); + free(error); + } } + + svec_destroy(&argv); } static int @@ -390,6 +455,10 @@ unixctl_server_run(struct unixctl_server *server) struct unixctl_conn *conn, *next; int i; + if (!server) { + return; + } + for (i = 0; i < 10; i++) { int fd = accept(server->fd, NULL, NULL); if (fd < 0) { @@ -401,8 +470,7 @@ unixctl_server_run(struct unixctl_server *server) new_connection(server, fd); } - LIST_FOR_EACH_SAFE (conn, next, - struct unixctl_conn, node, &server->conns) { + LIST_FOR_EACH_SAFE (conn, next, node, &server->conns) { int error = run_connection(conn); if (error && error != EAGAIN) { kill_connection(conn); @@ -415,8 +483,12 @@ unixctl_server_wait(struct unixctl_server *server) { struct unixctl_conn *conn; + if (!server) { + return; + } + poll_fd_wait(server->fd, POLLIN); - LIST_FOR_EACH (conn, struct unixctl_conn, node, &server->conns) { + LIST_FOR_EACH (conn, node, &server->conns) { if (conn->state == S_RECV) { poll_fd_wait(conn->fd, POLLIN); } else if (conn->state == S_SEND) { @@ -432,8 +504,7 @@ unixctl_server_destroy(struct unixctl_server *server) if (server) { struct unixctl_conn *conn, *next; - LIST_FOR_EACH_SAFE (conn, next, - struct unixctl_conn, node, &server->conns) { + LIST_FOR_EACH_SAFE (conn, next, node, &server->conns) { kill_connection(conn); } @@ -446,7 +517,7 @@ unixctl_server_destroy(struct unixctl_server *server) /* Connects to a Vlog server socket. 'path' should be the name of a Vlog * server socket. If it does not start with '/', it will be prefixed with - * ovs_rundir (e.g. /var/run). + * the rundir (e.g. /usr/local/var/run/openvswitch). * * Returns 0 if successful, otherwise a positive errno value. If successful, * sets '*clientp' to the new unixctl_client, otherwise to NULL. */ @@ -460,11 +531,7 @@ unixctl_client_create(const char *path, struct unixctl_client **clientp) /* Determine location. */ client = xmalloc(sizeof *client); - if (path[0] == '/') { - client->connect_path = xstrdup(path); - } else { - client->connect_path = xasprintf("%s/%s", ovs_rundir, path); - } + client->connect_path = abs_file_name(ovs_rundir(), path); client->bind_path = xasprintf("/tmp/vlog.%ld.%d", (long int) getpid(), counter++); @@ -544,8 +611,7 @@ unixctl_client_transact(struct unixctl_client *client, if (error) { VLOG_WARN("error reading reply from %s: %s", client->connect_path, - (error == EOF ? "unexpected end of file" - : strerror(error))); + ovs_retval_to_string(error)); goto error; }