netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / ovsdb / ovsdb-server.c
index bdcdad9..fa662b1 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <getopt.h>
 #include <inttypes.h>
 #include <signal.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include "column.h"
 #include "command-line.h"
 #include "daemon.h"
 #include "dirs.h"
-#include "dummy.h"
 #include "dynamic-string.h"
+#include "fatal-signal.h"
 #include "file.h"
 #include "hash.h"
 #include "json.h"
@@ -34,6 +35,7 @@
 #include "jsonrpc-server.h"
 #include "list.h"
 #include "memory.h"
+#include "monitor.h"
 #include "ovsdb.h"
 #include "ovsdb-data.h"
 #include "ovsdb-types.h"
@@ -52,7 +54,8 @@
 #include "trigger.h"
 #include "util.h"
 #include "unixctl.h"
-#include "vlog.h"
+#include "perf-counter.h"
+#include "openvswitch/vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(ovsdb_server);
 
@@ -75,6 +78,9 @@ static bool bootstrap_ca_cert;
 static unixctl_cb_func ovsdb_server_exit;
 static unixctl_cb_func ovsdb_server_compact;
 static unixctl_cb_func ovsdb_server_reconnect;
+static unixctl_cb_func ovsdb_server_perf_counters_clear;
+static unixctl_cb_func ovsdb_server_perf_counters_show;
+static unixctl_cb_func ovsdb_server_disable_monitor2;
 
 struct server_config {
     struct sset *remotes;
@@ -91,11 +97,12 @@ static unixctl_cb_func ovsdb_server_remove_database;
 static unixctl_cb_func ovsdb_server_list_databases;
 
 static char *open_db(struct server_config *config, const char *filename);
+static void close_db(struct db *db);
 
 static void parse_options(int *argc, char **argvp[],
                           struct sset *remotes, char **unixctl_pathp,
                           char **run_command);
-static void usage(void) NO_RETURN;
+OVS_NO_RETURN static void usage(void);
 
 static char *reconfigure_remotes(struct ovsdb_jsonrpc_server *,
                                  const struct shash *all_dbs,
@@ -113,6 +120,84 @@ static void save_config(struct server_config *);
 static void load_config(FILE *config_file, struct sset *remotes,
                         struct sset *db_filenames);
 
+static void
+main_loop(struct ovsdb_jsonrpc_server *jsonrpc, struct shash *all_dbs,
+          struct unixctl_server *unixctl, struct sset *remotes,
+          struct process *run_process, bool *exiting)
+{
+    char *remotes_error, *ssl_error;
+    struct shash_node *node;
+    long long int status_timer = LLONG_MIN;
+
+    *exiting = false;
+    ssl_error = NULL;
+    remotes_error = NULL;
+    while (!*exiting) {
+        memory_run();
+        if (memory_should_report()) {
+            struct simap usage;
+
+            simap_init(&usage);
+            ovsdb_jsonrpc_server_get_memory_usage(jsonrpc, &usage);
+            ovsdb_monitor_get_memory_usage(&usage);
+            SHASH_FOR_EACH(node, all_dbs) {
+                struct db *db = node->data;
+                ovsdb_get_memory_usage(db->db, &usage);
+            }
+            memory_report(&usage);
+            simap_destroy(&usage);
+        }
+
+        /* Run unixctl_server_run() before reconfigure_remotes() because
+         * ovsdb-server/add-remote and ovsdb-server/remove-remote can change
+         * the set of remotes that reconfigure_remotes() uses. */
+        unixctl_server_run(unixctl);
+
+        report_error_if_changed(
+            reconfigure_remotes(jsonrpc, all_dbs, remotes),
+            &remotes_error);
+        report_error_if_changed(reconfigure_ssl(all_dbs), &ssl_error);
+        ovsdb_jsonrpc_server_run(jsonrpc);
+
+        SHASH_FOR_EACH(node, all_dbs) {
+            struct db *db = node->data;
+            ovsdb_trigger_run(db->db, time_msec());
+        }
+        if (run_process) {
+            process_run();
+            if (process_exited(run_process)) {
+                *exiting = true;
+            }
+        }
+
+        /* update Manager status(es) every 5 seconds */
+        if (time_msec() >= status_timer) {
+            status_timer = time_msec() + 5000;
+            update_remote_status(jsonrpc, remotes, all_dbs);
+        }
+
+        memory_wait();
+        ovsdb_jsonrpc_server_wait(jsonrpc);
+        unixctl_server_wait(unixctl);
+        SHASH_FOR_EACH(node, all_dbs) {
+            struct db *db = node->data;
+            ovsdb_trigger_wait(db->db, time_msec());
+        }
+        if (run_process) {
+            process_wait(run_process);
+        }
+        if (*exiting) {
+            poll_immediate_wake();
+        }
+        poll_timer_wait_until(status_timer);
+        poll_block();
+        if (should_service_stop()) {
+            *exiting = true;
+        }
+    }
+
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -125,22 +210,21 @@ main(int argc, char *argv[])
     struct process *run_process;
     bool exiting;
     int retval;
-    long long int status_timer = LLONG_MIN;
     FILE *config_tmpfile;
     struct server_config server_config;
     struct shash all_dbs;
-    struct shash_node *node;
-    char *remotes_error, *ssl_error;
+    struct shash_node *node, *next;
     char *error;
     int i;
 
-    proctitle_init(argc, argv);
+    ovs_cmdl_proctitle_init(argc, argv);
     set_program_name(argv[0]);
     service_start(&argc, &argv);
-    signal(SIGPIPE, SIG_IGN);
+    fatal_ignore_sigpipe();
     process_init();
 
     parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command);
+    daemon_become_new_user(false);
 
     /* Create and initialize 'config_tmpfile' as a temporary file to hold
      * ovsdb-server's most basic configuration, and then save our initial
@@ -168,7 +252,7 @@ main(int argc, char *argv[])
 
     save_config__(config_tmpfile, &remotes, &db_filenames);
 
-    daemonize_start();
+    daemonize_start(false);
 
     /* Load the saved config. */
     load_config(config_tmpfile, &remotes, &db_filenames);
@@ -177,6 +261,9 @@ main(int argc, char *argv[])
     shash_init(&all_dbs);
     server_config.all_dbs = &all_dbs;
     server_config.jsonrpc = jsonrpc;
+
+    perf_counters_init();
+
     SSET_FOR_EACH (db_filename, &db_filenames) {
         error = open_db(&server_config, db_filename);
         if (error) {
@@ -241,78 +328,27 @@ main(int argc, char *argv[])
                              ovsdb_server_remove_database, &server_config);
     unixctl_command_register("ovsdb-server/list-dbs", "", 0, 0,
                              ovsdb_server_list_databases, &all_dbs);
+    unixctl_command_register("ovsdb-server/perf-counters-show", "", 0, 0,
+                             ovsdb_server_perf_counters_show, NULL);
+    unixctl_command_register("ovsdb-server/perf-counters-clear", "", 0, 0,
+                             ovsdb_server_perf_counters_clear, NULL);
 
-    exiting = false;
-    ssl_error = NULL;
-    remotes_error = NULL;
-    while (!exiting) {
-        memory_run();
-        if (memory_should_report()) {
-            struct simap usage;
-
-            simap_init(&usage);
-            ovsdb_jsonrpc_server_get_memory_usage(jsonrpc, &usage);
-            SHASH_FOR_EACH(node, &all_dbs) {
-                struct db *db = node->data;
-                ovsdb_get_memory_usage(db->db, &usage);
-            }
-            memory_report(&usage);
-            simap_destroy(&usage);
-        }
-
-        /* Run unixctl_server_run() before reconfigure_remotes() because
-         * ovsdb-server/add-remote and ovsdb-server/remove-remote can change
-         * the set of remotes that reconfigure_remotes() uses. */
-        unixctl_server_run(unixctl);
-
-        report_error_if_changed(
-            reconfigure_remotes(jsonrpc, &all_dbs, &remotes),
-            &remotes_error);
-        report_error_if_changed(reconfigure_ssl(&all_dbs), &ssl_error);
-        ovsdb_jsonrpc_server_run(jsonrpc);
-
-        SHASH_FOR_EACH(node, &all_dbs) {
-            struct db *db = node->data;
-            ovsdb_trigger_run(db->db, time_msec());
-        }
-        if (run_process) {
-            process_run();
-            if (process_exited(run_process)) {
-                exiting = true;
-            }
-        }
+    /* Simulate the behavior of OVS release prior to version 2.5 that
+     * does not support the monitor2 method.  */
+    unixctl_command_register("ovsdb-server/disable-monitor2", "", 0, 0,
+                             ovsdb_server_disable_monitor2, jsonrpc);
 
-        /* update Manager status(es) every 5 seconds */
-        if (time_msec() >= status_timer) {
-            status_timer = time_msec() + 5000;
-            update_remote_status(jsonrpc, &remotes, &all_dbs);
-        }
+    main_loop(jsonrpc, &all_dbs, unixctl, &remotes, run_process, &exiting);
 
-        memory_wait();
-        ovsdb_jsonrpc_server_wait(jsonrpc);
-        unixctl_server_wait(unixctl);
-        SHASH_FOR_EACH(node, &all_dbs) {
-            struct db *db = node->data;
-            ovsdb_trigger_wait(db->db, time_msec());
-        }
-        if (run_process) {
-            process_wait(run_process);
-        }
-        if (exiting) {
-            poll_immediate_wake();
-        }
-        poll_timer_wait_until(status_timer);
-        poll_block();
-        if (should_service_stop()) {
-            exiting = true;
-        }
-    }
     ovsdb_jsonrpc_server_destroy(jsonrpc);
-    SHASH_FOR_EACH(node, &all_dbs) {
+    SHASH_FOR_EACH_SAFE(node, next, &all_dbs) {
         struct db *db = node->data;
-        ovsdb_destroy(db->db);
+        close_db(db);
+        shash_delete(&all_dbs, node);
     }
+    shash_destroy(&all_dbs);
     sset_destroy(&remotes);
+    sset_destroy(&db_filenames);
     unixctl_server_destroy(unixctl);
 
     if (run_process && process_exited(run_process)) {
@@ -322,11 +358,49 @@ main(int argc, char *argv[])
                       run_command, process_status_msg(status));
         }
     }
-
+    perf_counters_destroy();
     service_stop();
     return 0;
 }
 
+/* Returns true if 'filename' is known to be already open as a database,
+ * false if not.
+ *
+ * "False negatives" are possible. */
+static bool
+is_already_open(struct server_config *config OVS_UNUSED,
+                const char *filename OVS_UNUSED)
+{
+#ifndef _WIN32
+    struct stat s;
+
+    if (!stat(filename, &s)) {
+        struct shash_node *node;
+
+        SHASH_FOR_EACH (node, config->all_dbs) {
+            struct db *db = node->data;
+            struct stat s2;
+
+            if (!stat(db->filename, &s2)
+                && s.st_dev == s2.st_dev
+                && s.st_ino == s2.st_ino) {
+                return true;
+            }
+        }
+    }
+#endif  /* !_WIN32 */
+
+    return false;
+}
+
+static void
+close_db(struct db *db)
+{
+    ovsdb_destroy(db->db);
+    free(db->filename);
+    free(db);
+}
+
 static char *
 open_db(struct server_config *config, const char *filename)
 {
@@ -334,6 +408,13 @@ open_db(struct server_config *config, const char *filename)
     struct db *db;
     char *error;
 
+    /* If we know that the file is already open, return a good error message.
+     * Otherwise, if the file is open, we'll fail later on with a harder to
+     * interpret file locking error. */
+    if (is_already_open(config, filename)) {
+        return xasprintf("%s: already open", filename);
+    }
+
     db = xzalloc(sizeof *db);
     db->filename = xstrdup(filename);
 
@@ -348,9 +429,7 @@ open_db(struct server_config *config, const char *filename)
     }
 
     ovsdb_error_destroy(db_error);
-    ovsdb_destroy(db->db);
-    free(db->filename);
-    free(db);
+    close_db(db);
     return error;
 }
 
@@ -369,7 +448,7 @@ find_db(const struct shash *all_dbs, const char *db_name)
     return NULL;
 }
 
-static char * WARN_UNUSED_RESULT
+static char * OVS_WARN_UNUSED_RESULT
 parse_db_column__(const struct shash *all_dbs,
                   const char *name_, char *name,
                   const struct db **dbp,
@@ -423,7 +502,7 @@ parse_db_column__(const struct shash *all_dbs,
 
 /* Returns NULL if successful, otherwise a malloc()'d string describing the
  * error. */
-static char * WARN_UNUSED_RESULT
+static char * OVS_WARN_UNUSED_RESULT
 parse_db_column(const struct shash *all_dbs,
                 const char *name_,
                 const struct db **dbp,
@@ -439,7 +518,7 @@ parse_db_column(const struct shash *all_dbs,
 
 /* Returns NULL if successful, otherwise a malloc()'d string describing the
  * error. */
-static char * WARN_UNUSED_RESULT
+static char * OVS_WARN_UNUSED_RESULT
 parse_db_string_column(const struct shash *all_dbs,
                        const char *name,
                        const struct db **dbp,
@@ -480,6 +559,7 @@ query_db_string(const struct shash *all_dbs, const char *name,
                                         &db, &table, &column);
         if (retval) {
             ds_put_format(errors, "%s\n", retval);
+            free(retval);
             return NULL;
         }
 
@@ -961,6 +1041,40 @@ ovsdb_server_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
     unixctl_command_reply(conn, NULL);
 }
 
+static void
+ovsdb_server_perf_counters_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                                const char *argv[] OVS_UNUSED,
+                                void *arg_ OVS_UNUSED)
+{
+    char *s = perf_counters_to_string();
+
+    unixctl_command_reply(conn, s);
+    free(s);
+}
+
+static void
+ovsdb_server_perf_counters_clear(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                                 const char *argv[] OVS_UNUSED,
+                                 void *arg_ OVS_UNUSED)
+{
+    perf_counters_clear();
+    unixctl_command_reply(conn, NULL);
+}
+
+/* "ovsdb-server/disable-monitor2": makes ovsdb-server drop all of its
+ * JSON-RPC connections and reconnect. New sessions will not recognize
+ * the 'monitor2' method.   */
+static void
+ovsdb_server_disable_monitor2(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                              const char *argv[] OVS_UNUSED, void *jsonrpc_)
+{
+    struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_;
+
+    ovsdb_jsonrpc_disable_monitor2();
+    ovsdb_jsonrpc_server_reconnect(jsonrpc);
+    unixctl_command_reply(conn, NULL);
+}
+
 static void
 ovsdb_server_compact(struct unixctl_conn *conn, int argc,
                      const char *argv[], void *dbs_)
@@ -1125,10 +1239,8 @@ ovsdb_server_remove_database(struct unixctl_conn *conn, int argc OVS_UNUSED,
     ok = ovsdb_jsonrpc_server_remove_db(config->jsonrpc, db->db);
     ovs_assert(ok);
 
-    ovsdb_destroy(db->db);
+    close_db(db);
     shash_delete(config->all_dbs, node);
-    free(db->filename);
-    free(db);
 
     save_config(config);
     unixctl_command_reply(conn, NULL);
@@ -1165,7 +1277,7 @@ parse_options(int *argcp, char **argvp[],
         OPT_UNIXCTL,
         OPT_RUN,
         OPT_BOOTSTRAP_CA_CERT,
-        OPT_ENABLE_DUMMY,
+        OPT_PEER_CA_CERT,
         VLOG_OPTION_ENUMS,
         DAEMON_OPTION_ENUMS
     };
@@ -1180,13 +1292,13 @@ parse_options(int *argcp, char **argvp[],
         DAEMON_LONG_OPTIONS,
         VLOG_LONG_OPTIONS,
         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
+        {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
         {"private-key", required_argument, NULL, 'p'},
         {"certificate", required_argument, NULL, 'c'},
         {"ca-cert",     required_argument, NULL, 'C'},
-        {"enable-dummy", optional_argument, NULL, OPT_ENABLE_DUMMY},
         {NULL, 0, NULL, 0},
     };
-    char *short_options = long_options_to_short_options(long_options);
+    char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
     int argc = *argcp;
     char **argv = *argvp;
 
@@ -1240,8 +1352,8 @@ parse_options(int *argcp, char **argvp[],
             bootstrap_ca_cert = true;
             break;
 
-        case OPT_ENABLE_DUMMY:
-            dummy_enable(optarg && !strcmp(optarg, "override"));
+        case OPT_PEER_CA_CERT:
+            stream_ssl_set_peer_ca_cert_file(optarg);
             break;
 
         case '?':