+parse_db_string_column(const struct ovsdb *db,
+ const char *name_,
+ const struct ovsdb_table **tablep,
+ const struct ovsdb_column **columnp)
+{
+ char *name, *table_name, *column_name;
+ const struct ovsdb_column *column;
+ const struct ovsdb_table *table;
+ char *save_ptr = NULL;
+
+ name = xstrdup(name_);
+ strtok_r(name, ":", &save_ptr); /* "db:" */
+ table_name = strtok_r(NULL, ",", &save_ptr);
+ column_name = strtok_r(NULL, ",", &save_ptr);
+ if (!table_name || !column_name) {
+ ovs_fatal(0, "\"%s\": invalid syntax", name_);
+ }
+
+ table = ovsdb_get_table(db, table_name);
+ if (!table) {
+ ovs_fatal(0, "\"%s\": no table named %s", name_, table_name);
+ }
+
+ column = ovsdb_table_schema_get_column(table->schema, column_name);
+ if (!column) {
+ ovs_fatal(0, "\"%s\": table \"%s\" has no column \"%s\"",
+ name_, table_name, column_name);
+ }
+ free(name);
+
+ if (column->type.key.type != OVSDB_TYPE_STRING
+ || column->type.value.type != OVSDB_TYPE_VOID) {
+ ovs_fatal(0, "\"%s\": table \"%s\" column \"%s\" is "
+ "not string or set of strings",
+ name_, table->schema->name, column->name);
+ }
+
+ *columnp = column;
+ *tablep = table;
+}
+
+#if HAVE_OPENSSL
+static const char *
+query_db_string(const struct ovsdb *db, const char *name)
+{
+ if (!name || strncmp(name, "db:", 3)) {
+ return name;
+ } else {
+ const struct ovsdb_column *column;
+ const struct ovsdb_table *table;
+ const struct ovsdb_row *row;
+
+ parse_db_string_column(db, name, &table, &column);
+
+ HMAP_FOR_EACH (row, hmap_node, &table->rows) {
+ const struct ovsdb_datum *datum;
+ size_t i;
+
+ datum = &row->fields[column->index];
+ for (i = 0; i < datum->n; i++) {
+ if (datum->keys[i].string[0]) {
+ return datum->keys[i].string;
+ }
+ }
+ }
+ return NULL;
+ }
+}
+#endif /* HAVE_OPENSSL */
+
+static void
+query_db_remotes(const char *name, const struct ovsdb *db,
+ struct shash *remotes)
+{
+ const struct ovsdb_column *column;
+ const struct ovsdb_table *table;
+ const struct ovsdb_row *row;
+
+ parse_db_string_column(db, name, &table, &column);
+
+ HMAP_FOR_EACH (row, hmap_node, &table->rows) {
+ const struct ovsdb_datum *datum;
+ size_t i;
+
+ datum = &row->fields[column->index];
+ for (i = 0; i < datum->n; i++) {
+ shash_add_once(remotes, datum->keys[i].string, NULL);
+ }
+ }
+}
+
+/* Reconfigures ovsdb-server based on information in the database. */
+static void
+reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
+ const struct ovsdb *db, struct shash *remotes)
+{
+ struct shash resolved_remotes;
+ struct shash_node *node;
+
+ /* Configure remotes. */
+ shash_init(&resolved_remotes);
+ SHASH_FOR_EACH (node, remotes) {
+ const char *name = node->name;
+
+ if (!strncmp(name, "db:", 3)) {
+ query_db_remotes(name, db, &resolved_remotes);
+ } else {
+ shash_add_once(&resolved_remotes, name, NULL);
+ }
+ }
+ ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes);
+ shash_destroy(&resolved_remotes);
+
+#if HAVE_OPENSSL
+ /* Configure SSL. */
+ stream_ssl_set_key_and_cert(query_db_string(db, private_key_file),
+ query_db_string(db, certificate_file));
+ stream_ssl_set_ca_cert_file(query_db_string(db, ca_cert_file),
+ bootstrap_ca_cert);
+#endif
+}
+
+static void
+ovsdb_server_exit(struct unixctl_conn *conn, const char *args OVS_UNUSED,