@@ -20,6 +20,7 @@
#include <stdio.h>
#include "command-line.h"
+#include "daemon.h"
#include "db-ctl-base.h"
#include "dirs.h"
#include "fatal-signal.h"
@@ -37,6 +38,7 @@
#include "svec.h"
#include "table.h"
#include "timeval.h"
+#include "unixctl.h"
#include "util.h"
#include "openvswitch/vlog.h"
@@ -79,6 +81,13 @@ OVS_NO_RETURN static void nbctl_exit(int status);
/* --leader-only, --no-leader-only: Only accept the leader in a cluster. */
static int leader_only = true;
+/* --unixctl-path: Path to use for unixctl server, for "monitor" and "snoop"
+ commands. */
+static char *unixctl_path;
+
+static unixctl_cb_func server_cmd_exit;
+static unixctl_cb_func server_cmd_run;
+
static void nbctl_cmd_init(void);
OVS_NO_RETURN static void usage(void);
static void parse_options(int argc, char *argv[], struct shash *local_options);
@@ -93,14 +102,13 @@ static char * OVS_WARN_UNUSED_RESULT main_loop(const char *args, struct
ctl_command *commands,
size_t n_commands,
struct ovsdb_idl *idl);
+static void server_loop(struct ovsdb_idl *idl);
int
main(int argc, char *argv[])
{
struct ovsdb_idl *idl;
- struct ctl_command *commands;
struct shash local_options;
- size_t n_commands;
set_program_name(argv[0]);
fatal_ignore_sigpipe();
@@ -113,35 +121,51 @@ main(int argc, char *argv[])
char *args = process_escape_args(argv);
shash_init(&local_options);
parse_options(argc, argv, &local_options);
- commands = ctl_parse_commands(argc - optind, argv + optind, &local_options,
- &n_commands);
- VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG,
- "Called as %s", args);
-
- if (timeout) {
- time_alarm(timeout);
- }
+ argc -= optind;
+ argv += optind;
/* Initialize IDL. */
idl = the_idl = ovsdb_idl_create(db, &nbrec_idl_class, true, false);
ovsdb_idl_set_leader_only(idl, leader_only);
- run_prerequisites(commands, n_commands, idl);
- char *error = main_loop(args, commands, n_commands, idl);
- if (error) {
- ctl_fatal("%s", error);
+ if (get_detach()) {
+ if (argc != 0) {
+ ctl_fatal("non-option arguments not supported with --detach "
+ "(use --help for help)");
+ }
+ server_loop(idl);
+ } else {
+ struct ctl_command *commands;
+ size_t n_commands;
+
+ commands = ctl_parse_commands(argc, argv, &local_options, &n_commands);
+ VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG,
+ "Called as %s", args);
+
+ if (timeout) {
+ time_alarm(timeout);
+ }
+
+ run_prerequisites(commands, n_commands, idl);
+
+ char *error = main_loop(args, commands, n_commands, idl);
+ if (error) {
+ ctl_fatal("%s", error);
+ }
+
+ struct ctl_command *c;
+ for (c = commands; c < &commands[n_commands]; c++) {
+ ds_destroy(&c->output);
+ table_destroy(c->table);
+ free(c->table);
+ shash_destroy_free_data(&c->options);
+ }
+ free(commands);
}
ovsdb_idl_destroy(idl);
idl = the_idl = NULL;
- for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) {
- ds_destroy(&c->output);
- table_destroy(c->table);
- free(c->table);
- shash_destroy_free_data(&c->options);
- }
- free(commands);
free(args);
exit(EXIT_SUCCESS);
}
@@ -151,6 +175,7 @@ main_loop(const char *args, struct ctl_command *commands, size_t n_commands,
struct ovsdb_idl *idl)
{
unsigned int seqno;
+ bool idl_ready;
/* Execute the commands.
*
@@ -160,6 +185,11 @@ main_loop(const char *args, struct ctl_command *commands, size_t n_commands,
* it's because the database changed and we need to obtain an up-to-date
* view of the database before we try the transaction again. */
seqno = ovsdb_idl_get_seqno(idl);
+
+ /* IDL might have already obtained the database copy during previous
+ * invocation. If so, we can't expect the sequence number to change before
+ * we issue any new requests. */
+ idl_ready = ovsdb_idl_has_ever_connected(idl);
for (;;) {
ovsdb_idl_run(idl);
if (!ovsdb_idl_is_alive(idl)) {
@@ -168,7 +198,8 @@ main_loop(const char *args, struct ctl_command *commands, size_t n_commands,
db, ovs_retval_to_string(retval));
}
- if (seqno != ovsdb_idl_get_seqno(idl)) {
+ if (idl_ready || seqno != ovsdb_idl_get_seqno(idl)) {
+ idl_ready = false;
seqno = ovsdb_idl_get_seqno(idl);
bool retry;
@@ -204,6 +235,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
OPT_COMMANDS,
OPT_OPTIONS,
OPT_BOOTSTRAP_CA_CERT,
+ DAEMON_OPTION_ENUMS,
VLOG_OPTION_ENUMS,
TABLE_OPTION_ENUMS,
SSL_OPTION_ENUMS,
@@ -222,6 +254,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
{"leader-only", no_argument, &leader_only, true},
{"no-leader-only", no_argument, &leader_only, false},
{"version", no_argument, NULL, 'V'},
+ DAEMON_LONG_OPTIONS,
VLOG_LONG_OPTIONS,
STREAM_SSL_LONG_OPTIONS,
{"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
@@ -326,6 +359,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
}
break;
+ DAEMON_OPTION_HANDLERS
VLOG_OPTION_HANDLERS
TABLE_OPTION_HANDLERS(&table_style)
STREAM_SSL_OPTION_HANDLERS
@@ -519,6 +553,7 @@ Options:\n\
program_name, program_name, ctl_get_db_cmd_usage(),
ctl_list_db_tables_usage(), default_nb_db());
table_usage();
+ daemon_usage();
vlog_usage();
printf("\
--no-syslog equivalent to --verbose=nbctl:syslog:warn\n");
@@ -4529,3 +4564,104 @@ nbctl_cmd_init(void)
ctl_init(&nbrec_idl_class, nbrec_table_classes, tables, NULL, nbctl_exit);
ctl_register_commands(nbctl_commands);
}
+
+static void
+server_cmd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *exiting_)
+{
+ bool *exiting = exiting_;
+ *exiting = true;
+ unixctl_command_reply(conn, NULL);
+}
+
+static void
+server_cmd_run(struct unixctl_conn *conn, int argc, const char **argv_,
+ void *idl_)
+{
+ struct ovsdb_idl *idl = idl_;
+ struct ctl_command *commands = NULL;
+ struct shash local_options;
+ size_t n_commands = 0;
+ char *error = NULL;
+
+ /* Copy args so that getopt() can permute them. Leave last entry NULL. */
+ char **argv = xcalloc(argc + 1, sizeof *argv);
+ for (int i = 0; i < argc; i++) {
+ argv[i] = xstrdup(argv_[i]);
+ }
+
+ /* Parse commands & options. */
+ char *args = process_escape_args(argv);
+ shash_init(&local_options);
+ optind = 0;
+ parse_options(argc, argv, &local_options);
+ commands = ctl_parse_commands(argc - optind, argv + optind,
+ &local_options, &n_commands);
+ VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG,
+ "Running command %s", args);
+
+ /* TODO: Timeout handling. */
+ error = main_loop(args, commands, n_commands, idl);
+ if (error) {
+ unixctl_command_reply_error(conn, error);
+ goto out;
+ }
+
+ struct ds output = DS_EMPTY_INITIALIZER;
+ for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) {
+ /* TODO: Support for table output. */
+ ds_put_cstr(&output, ds_cstr_ro(&c->output));
+ }
+ unixctl_command_reply(conn, ds_cstr_ro(&output));
+ ds_destroy(&output);
+
+out:
+ free(error);
+ for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) {
+ ds_destroy(&c->output);
+ table_destroy(c->table);
+ free(c->table);
+ shash_destroy_free_data(&c->options);
+ }
+ free(commands);
+ shash_destroy_free_data(&local_options);
+ free(args);
+ for (int i = 0; i < argc; i++) {
+ free(argv[i]);
+ }
+ free(argv);
+}
+
+static void
+server_cmd_init(struct ovsdb_idl *idl, bool *exiting)
+{
+ unixctl_command_register("exit", "", 0, 0, server_cmd_exit, exiting);
+ unixctl_command_register("run", "", 1, INT_MAX, server_cmd_run, idl);
+}
+
+static void
+server_loop(struct ovsdb_idl *idl)
+{
+ struct unixctl_server *server = NULL;
+ bool exiting = false;
+
+ daemonize_start(false);
+ int error = unixctl_server_create(unixctl_path, &server);
+ if (error) {
+ ctl_fatal("failed to create unixctl server (%s)",
+ ovs_retval_to_string(error));
+ }
+ server_cmd_init(idl, &exiting);
+
+ for (;;) {
+ unixctl_server_run(server);
+ daemonize_complete();
+ unixctl_server_wait(server);
+ if (exiting) {
+ break;
+ }
+ poll_block();
+ }
+
+ unixctl_server_destroy(server);
+}
Make ovn-nbctl act as a unixctl server if we were asked to detach. This turns ovn-nbctl into a long-lived process that acts a proxy for interacting with NB DB. The main difference to regular mode of ovn-nbctl is that in the daemon mode, a local copy of database contents has to be obtained only once. Just two unixctl commands are supported 'run' and 'exit'. The former can be used to run any ovn-nbctl command or a batch of them as so: ovs-appctl -t ovn-nbctl run [OPTIONS] COMMAND [-- [OPTIONS] COMMAND] ... Commands that use tabular output ('find' and 'list') are not supported. As are --dry-run, --timeout, and --wait ovn-nbctl options. Also, running commands that have not yet been converted to not use ctl_fatal() will result in death of the daemon process. However, --monitor option can be used to keep the daemon running. Signed-off-by: Jakub Sitnicki <jkbs@redhat.com> --- ovn/utilities/ovn-nbctl.c | 180 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 158 insertions(+), 22 deletions(-)