1 /* Copyright (c) 2009, 2010 Nicira Networks
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
26 #include "command-line.h"
31 #include "jsonrpc-server.h"
32 #include "leak-checker.h"
34 #include "ovsdb-data.h"
35 #include "ovsdb-types.h"
36 #include "ovsdb-error.h"
37 #include "poll-loop.h"
40 #include "stream-ssl.h"
51 VLOG_DEFINE_THIS_MODULE(ovsdb_server);
54 /* SSL configuration. */
55 static char *private_key_file;
56 static char *certificate_file;
57 static char *ca_cert_file;
58 static bool bootstrap_ca_cert;
61 static unixctl_cb_func ovsdb_server_exit;
62 static unixctl_cb_func ovsdb_server_compact;
63 static unixctl_cb_func ovsdb_server_reconnect;
65 static void parse_options(int argc, char *argv[], char **file_namep,
66 struct shash *remotes, char **unixctl_pathp,
68 static void usage(void) NO_RETURN;
70 static void reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
71 const struct ovsdb *db, struct shash *remotes);
74 main(int argc, char *argv[])
76 char *unixctl_path = NULL;
77 char *run_command = NULL;
78 struct unixctl_server *unixctl;
79 struct ovsdb_jsonrpc_server *jsonrpc;
81 struct ovsdb_error *error;
82 struct ovsdb_file *file;
84 struct process *run_process;
89 proctitle_init(argc, argv);
90 set_program_name(argv[0]);
91 stress_init_command();
92 signal(SIGPIPE, SIG_IGN);
95 parse_options(argc, argv, &file_name, &remotes, &unixctl_path,
98 die_if_already_running();
101 error = ovsdb_file_open(file_name, false, &db, &file);
103 ovs_fatal(0, "%s", ovsdb_error_to_string(error));
106 jsonrpc = ovsdb_jsonrpc_server_create(db);
107 reconfigure_from_db(jsonrpc, db, &remotes);
109 retval = unixctl_server_create(unixctl_path, &unixctl);
117 run_argv[0] = "/bin/sh";
119 run_argv[2] = run_command;
122 retval = process_start(run_argv, NULL, 0, NULL, 0, &run_process);
124 ovs_fatal(retval, "%s: process failed to start", run_command);
130 daemonize_complete();
132 unixctl_command_register("exit", ovsdb_server_exit, &exiting);
133 unixctl_command_register("ovsdb-server/compact", ovsdb_server_compact,
135 unixctl_command_register("ovsdb-server/reconnect", ovsdb_server_reconnect,
140 reconfigure_from_db(jsonrpc, db, &remotes);
141 ovsdb_jsonrpc_server_run(jsonrpc);
142 unixctl_server_run(unixctl);
143 ovsdb_trigger_run(db, time_msec());
144 if (run_process && process_exited(run_process)) {
148 ovsdb_jsonrpc_server_wait(jsonrpc);
149 unixctl_server_wait(unixctl);
150 ovsdb_trigger_wait(db, time_msec());
152 process_wait(run_process);
155 poll_immediate_wake();
159 ovsdb_jsonrpc_server_destroy(jsonrpc);
161 shash_destroy(&remotes);
162 unixctl_server_destroy(unixctl);
164 if (run_process && process_exited(run_process)) {
165 int status = process_status(run_process);
167 ovs_fatal(0, "%s: child exited, %s",
168 run_command, process_status_msg(status));
176 parse_db_column(const struct ovsdb *db,
178 const struct ovsdb_table **tablep,
179 const struct ovsdb_column **columnp)
181 char *name, *table_name, *column_name;
182 const struct ovsdb_column *column;
183 const struct ovsdb_table *table;
184 char *save_ptr = NULL;
186 name = xstrdup(name_);
187 strtok_r(name, ":", &save_ptr); /* "db:" */
188 table_name = strtok_r(NULL, ",", &save_ptr);
189 column_name = strtok_r(NULL, ",", &save_ptr);
190 if (!table_name || !column_name) {
191 ovs_fatal(0, "\"%s\": invalid syntax", name_);
194 table = ovsdb_get_table(db, table_name);
196 ovs_fatal(0, "\"%s\": no table named %s", name_, table_name);
199 column = ovsdb_table_schema_get_column(table->schema, column_name);
201 ovs_fatal(0, "\"%s\": table \"%s\" has no column \"%s\"",
202 name_, table_name, column_name);
211 parse_db_string_column(const struct ovsdb *db,
213 const struct ovsdb_table **tablep,
214 const struct ovsdb_column **columnp)
216 const struct ovsdb_column *column;
217 const struct ovsdb_table *table;
219 parse_db_column(db, name, &table, &column);
221 if (column->type.key.type != OVSDB_TYPE_STRING
222 || column->type.value.type != OVSDB_TYPE_VOID) {
223 ovs_fatal(0, "\"%s\": table \"%s\" column \"%s\" is "
224 "not string or set of strings",
225 name, table->schema->name, column->name);
234 query_db_string(const struct ovsdb *db, const char *name)
236 if (!name || strncmp(name, "db:", 3)) {
239 const struct ovsdb_column *column;
240 const struct ovsdb_table *table;
241 const struct ovsdb_row *row;
243 parse_db_string_column(db, name, &table, &column);
245 HMAP_FOR_EACH (row, hmap_node, &table->rows) {
246 const struct ovsdb_datum *datum;
249 datum = &row->fields[column->index];
250 for (i = 0; i < datum->n; i++) {
251 if (datum->keys[i].string[0]) {
252 return datum->keys[i].string;
259 #endif /* HAVE_OPENSSL */
261 static struct ovsdb_jsonrpc_options *
262 add_remote(struct shash *remotes, const char *target)
264 struct ovsdb_jsonrpc_options *options;
266 options = shash_find_data(remotes, target);
268 options = ovsdb_jsonrpc_default_options();
269 shash_add(remotes, target, options);
275 static const union ovsdb_atom *
276 read_column(const struct ovsdb_row *row, const char *column_name,
277 enum ovsdb_atomic_type type)
279 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
280 const struct ovsdb_table_schema *schema = row->table->schema;
281 const struct ovsdb_column *column;
282 const struct ovsdb_datum *datum;
284 column = ovsdb_table_schema_get_column(schema, column_name);
286 VLOG_DBG_RL(&rl, "Table `%s' has no `%s' column",
287 schema->name, column_name);
291 if (column->type.key.type != type
292 || column->type.value.type != OVSDB_TYPE_VOID
293 || column->type.n_max != 1) {
294 if (!VLOG_DROP_DBG(&rl)) {
295 char *type_name = ovsdb_type_to_english(&column->type);
296 VLOG_DBG("Table `%s' column `%s' has type %s, not expected "
297 "type %s.", schema->name, column_name, type_name,
298 ovsdb_atomic_type_to_string(type));
303 datum = &row->fields[column->index];
304 return datum->n ? datum->keys : NULL;
308 read_integer_column(const struct ovsdb_row *row, const char *column_name,
309 long long int *integerp)
311 const union ovsdb_atom *atom;
313 atom = read_column(row, column_name, OVSDB_TYPE_INTEGER);
314 *integerp = atom ? atom->integer : 0;
319 read_string_column(const struct ovsdb_row *row, const char *column_name,
320 const char **stringp)
322 const union ovsdb_atom *atom;
324 atom = read_column(row, column_name, OVSDB_TYPE_STRING);
325 *stringp = atom ? atom->string : 0;
329 /* Adds a remote and options to 'remotes', based on the Manager table row in
332 add_manager_options(struct shash *remotes, const struct ovsdb_row *row)
334 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
335 struct ovsdb_jsonrpc_options *options;
336 long long int max_backoff, probe_interval;
339 if (!read_string_column(row, "target", &target) || !target) {
340 VLOG_INFO_RL(&rl, "Table `%s' has missing or invalid `target' column",
341 row->table->schema->name);
345 options = add_remote(remotes, target);
346 if (read_integer_column(row, "max_backoff", &max_backoff)) {
347 options->max_backoff = max_backoff;
349 if (read_integer_column(row, "inactivity_probe", &probe_interval)) {
350 options->probe_interval = probe_interval;
355 query_db_remotes(const char *name, const struct ovsdb *db,
356 struct shash *remotes)
358 const struct ovsdb_column *column;
359 const struct ovsdb_table *table;
360 const struct ovsdb_row *row;
362 parse_db_column(db, name, &table, &column);
364 if (column->type.key.type == OVSDB_TYPE_STRING
365 && column->type.value.type == OVSDB_TYPE_VOID) {
366 HMAP_FOR_EACH (row, hmap_node, &table->rows) {
367 const struct ovsdb_datum *datum;
370 datum = &row->fields[column->index];
371 for (i = 0; i < datum->n; i++) {
372 add_remote(remotes, datum->keys[i].string);
375 } else if (column->type.key.type == OVSDB_TYPE_UUID
376 && column->type.key.u.uuid.refTable
377 && column->type.value.type == OVSDB_TYPE_VOID) {
378 const struct ovsdb_table *ref_table = column->type.key.u.uuid.refTable;
379 HMAP_FOR_EACH (row, hmap_node, &table->rows) {
380 const struct ovsdb_datum *datum;
383 datum = &row->fields[column->index];
384 for (i = 0; i < datum->n; i++) {
385 const struct ovsdb_row *ref_row;
387 ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid);
389 add_manager_options(remotes, ref_row);
396 /* Reconfigures ovsdb-server based on information in the database. */
398 reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
399 const struct ovsdb *db, struct shash *remotes)
401 struct shash resolved_remotes;
402 struct shash_node *node;
404 /* Configure remotes. */
405 shash_init(&resolved_remotes);
406 SHASH_FOR_EACH (node, remotes) {
407 const char *name = node->name;
409 if (!strncmp(name, "db:", 3)) {
410 query_db_remotes(name, db, &resolved_remotes);
412 add_remote(&resolved_remotes, name);
415 ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes);
416 shash_destroy(&resolved_remotes);
420 stream_ssl_set_key_and_cert(query_db_string(db, private_key_file),
421 query_db_string(db, certificate_file));
422 stream_ssl_set_ca_cert_file(query_db_string(db, ca_cert_file),
428 ovsdb_server_exit(struct unixctl_conn *conn, const char *args OVS_UNUSED,
431 bool *exiting = exiting_;
433 unixctl_command_reply(conn, 200, NULL);
437 ovsdb_server_compact(struct unixctl_conn *conn, const char *args OVS_UNUSED,
440 struct ovsdb_file *file = file_;
441 struct ovsdb_error *error;
443 VLOG_INFO("compacting database by user request");
444 error = ovsdb_file_compact(file);
446 unixctl_command_reply(conn, 200, NULL);
448 char *s = ovsdb_error_to_string(error);
449 ovsdb_error_destroy(error);
450 unixctl_command_reply(conn, 503, s);
455 /* "ovsdb-server/reconnect": makes ovsdb-server drop all of its JSON-RPC
456 * connections and reconnect. */
458 ovsdb_server_reconnect(struct unixctl_conn *conn, const char *args OVS_UNUSED,
461 struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_;
463 ovsdb_jsonrpc_server_reconnect(jsonrpc);
464 unixctl_command_reply(conn, 200, NULL);
468 parse_options(int argc, char *argv[], char **file_namep,
469 struct shash *remotes, char **unixctl_pathp,
473 OPT_DUMMY = UCHAR_MAX + 1,
477 OPT_BOOTSTRAP_CA_CERT,
479 LEAK_CHECKER_OPTION_ENUMS
481 static struct option long_options[] = {
482 {"remote", required_argument, 0, OPT_REMOTE},
483 {"unixctl", required_argument, 0, OPT_UNIXCTL},
484 {"run", required_argument, 0, OPT_RUN},
485 {"help", no_argument, 0, 'h'},
486 {"version", no_argument, 0, 'V'},
489 LEAK_CHECKER_LONG_OPTIONS,
491 {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
492 {"private-key", required_argument, 0, 'p'},
493 {"certificate", required_argument, 0, 'c'},
494 {"ca-cert", required_argument, 0, 'C'},
498 char *short_options = long_options_to_short_options(long_options);
504 c = getopt_long(argc, argv, short_options, long_options, NULL);
511 shash_add_once(remotes, optarg, NULL);
515 *unixctl_pathp = optarg;
519 *run_command = optarg;
526 OVS_PRINT_VERSION(0, 0);
530 DAEMON_OPTION_HANDLERS
531 LEAK_CHECKER_OPTION_HANDLERS
535 private_key_file = optarg;
539 certificate_file = optarg;
543 ca_cert_file = optarg;
544 bootstrap_ca_cert = false;
547 case OPT_BOOTSTRAP_CA_CERT:
548 ca_cert_file = optarg;
549 bootstrap_ca_cert = true;
566 ovs_fatal(0, "database file is only non-option argument; "
567 "use --help for usage");
568 } else if (argc < 1) {
569 ovs_fatal(0, "missing database file argument; use --help for usage");
572 *file_namep = argv[0];
578 printf("%s: Open vSwitch database server\n"
579 "usage: %s [OPTIONS] DATABASE\n"
580 "where DATABASE is a database file in ovsdb format.\n",
581 program_name, program_name);
582 printf("\nJSON-RPC options (may be specified any number of times):\n"
583 " --remote=REMOTE connect or listen to REMOTE\n");
584 stream_usage("JSON-RPC", true, true, true);
587 printf("\nOther options:\n"
588 " --run COMMAND run COMMAND as subprocess then exit\n"
589 " --unixctl=SOCKET override default control socket name\n"
590 " -h, --help display this help message\n"
591 " -V, --version display version information\n");
592 leak_checker_usage();