]>
Commit | Line | Data |
---|---|---|
b421d2af | 1 | /* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. |
f85f8ebb BP |
2 | * |
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: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
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. | |
14 | */ | |
15 | ||
16 | #include <config.h> | |
17 | ||
f85f8ebb BP |
18 | #include <errno.h> |
19 | #include <getopt.h> | |
20 | #include <signal.h> | |
eb077b26 | 21 | #include <unistd.h> |
f85f8ebb | 22 | |
0b1fae1b | 23 | #include "column.h" |
f85f8ebb BP |
24 | #include "command-line.h" |
25 | #include "daemon.h" | |
29701194 | 26 | #include "dirs.h" |
06834871 | 27 | #include "dummy.h" |
b4e8d170 | 28 | #include "dynamic-string.h" |
bd06962a | 29 | #include "file.h" |
da897f41 | 30 | #include "hash.h" |
f85f8ebb BP |
31 | #include "json.h" |
32 | #include "jsonrpc.h" | |
33 | #include "jsonrpc-server.h" | |
34 | #include "leak-checker.h" | |
35 | #include "list.h" | |
0d085684 | 36 | #include "memory.h" |
da897f41 | 37 | #include "ovsdb.h" |
0b1fae1b BP |
38 | #include "ovsdb-data.h" |
39 | #include "ovsdb-types.h" | |
f85f8ebb BP |
40 | #include "ovsdb-error.h" |
41 | #include "poll-loop.h" | |
42 | #include "process.h" | |
0b1fae1b | 43 | #include "row.h" |
0d085684 | 44 | #include "simap.h" |
9467fe62 | 45 | #include "stream-ssl.h" |
f85f8ebb | 46 | #include "stream.h" |
cc01d0bb | 47 | #include "stress.h" |
b3c01ed3 | 48 | #include "sset.h" |
0b1fae1b | 49 | #include "table.h" |
f85f8ebb | 50 | #include "timeval.h" |
0b3e7a8b | 51 | #include "transaction.h" |
f85f8ebb BP |
52 | #include "trigger.h" |
53 | #include "util.h" | |
54 | #include "unixctl.h" | |
f85f8ebb | 55 | #include "vlog.h" |
5136ce49 | 56 | |
d98e6007 | 57 | VLOG_DEFINE_THIS_MODULE(ovsdb_server); |
f85f8ebb | 58 | |
b4e8d170 BP |
59 | struct db { |
60 | /* Initialized in main(). */ | |
61 | char *filename; | |
62 | struct ovsdb_file *file; | |
63 | struct ovsdb *db; | |
64 | ||
65 | /* Only used by update_remote_status(). */ | |
66 | struct ovsdb_txn *txn; | |
67 | }; | |
68 | ||
78876719 BP |
69 | /* SSL configuration. */ |
70 | static char *private_key_file; | |
71 | static char *certificate_file; | |
72 | static char *ca_cert_file; | |
73 | static bool bootstrap_ca_cert; | |
74 | ||
aa78de9d | 75 | static unixctl_cb_func ovsdb_server_exit; |
ada496b5 | 76 | static unixctl_cb_func ovsdb_server_compact; |
31d0b6c9 | 77 | static unixctl_cb_func ovsdb_server_reconnect; |
aa78de9d | 78 | |
b421d2af BP |
79 | struct add_remote_aux { |
80 | struct sset *remotes; | |
81 | struct db *dbs; | |
82 | size_t n_dbs; | |
83 | }; | |
84 | static unixctl_cb_func ovsdb_server_add_remote; | |
85 | static unixctl_cb_func ovsdb_server_remove_remote; | |
86 | static unixctl_cb_func ovsdb_server_list_remotes; | |
87 | ||
b4e8d170 | 88 | static void parse_options(int *argc, char **argvp[], |
b3c01ed3 | 89 | struct sset *remotes, char **unixctl_pathp, |
475afa1b | 90 | char **run_command); |
f85f8ebb BP |
91 | static void usage(void) NO_RETURN; |
92 | ||
78876719 | 93 | static void reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc, |
b4e8d170 BP |
94 | const struct db dbs[], size_t n_dbs, |
95 | struct sset *remotes); | |
0b1fae1b | 96 | |
0b3e7a8b | 97 | static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc, |
b3c01ed3 | 98 | const struct sset *remotes, |
b4e8d170 | 99 | struct db dbs[], size_t n_dbs); |
0b3e7a8b | 100 | |
f85f8ebb BP |
101 | int |
102 | main(int argc, char *argv[]) | |
103 | { | |
aa78de9d | 104 | char *unixctl_path = NULL; |
475afa1b | 105 | char *run_command = NULL; |
f85f8ebb BP |
106 | struct unixctl_server *unixctl; |
107 | struct ovsdb_jsonrpc_server *jsonrpc; | |
b3c01ed3 | 108 | struct sset remotes; |
475afa1b | 109 | struct process *run_process; |
aa78de9d | 110 | bool exiting; |
f85f8ebb | 111 | int retval; |
0b3e7a8b | 112 | long long int status_timer = LLONG_MIN; |
b421d2af | 113 | struct add_remote_aux add_remote_aux; |
f85f8ebb | 114 | |
b4e8d170 BP |
115 | struct db *dbs; |
116 | int n_dbs; | |
117 | int i; | |
118 | ||
40f0707c | 119 | proctitle_init(argc, argv); |
f85f8ebb | 120 | set_program_name(argv[0]); |
cc01d0bb | 121 | stress_init_command(); |
f85f8ebb BP |
122 | signal(SIGPIPE, SIG_IGN); |
123 | process_init(); | |
124 | ||
b4e8d170 | 125 | parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command); |
f85f8ebb | 126 | |
95440284 | 127 | daemonize_start(); |
eb077b26 | 128 | |
b4e8d170 BP |
129 | n_dbs = MAX(1, argc); |
130 | dbs = xcalloc(n_dbs + 1, sizeof *dbs); | |
131 | if (argc > 0) { | |
132 | for (i = 0; i < argc; i++) { | |
133 | dbs[i].filename = argv[i]; | |
134 | } | |
135 | } else { | |
136 | dbs[0].filename = xasprintf("%s/conf.db", ovs_dbdir()); | |
f85f8ebb BP |
137 | } |
138 | ||
b4e8d170 BP |
139 | for (i = 0; i < n_dbs; i++) { |
140 | struct ovsdb_error *error; | |
141 | ||
142 | error = ovsdb_file_open(dbs[i].filename, false, | |
143 | &dbs[i].db, &dbs[i].file); | |
144 | if (error) { | |
145 | ovs_fatal(0, "%s", ovsdb_error_to_string(error)); | |
146 | } | |
147 | } | |
148 | ||
149 | jsonrpc = ovsdb_jsonrpc_server_create(); | |
150 | for (i = 0; i < n_dbs; i++) { | |
151 | if (!ovsdb_jsonrpc_server_add_db(jsonrpc, dbs[i].db)) { | |
152 | ovs_fatal(0, "%s: duplicate database name", | |
153 | dbs[i].db->schema->name); | |
154 | } | |
155 | } | |
156 | reconfigure_from_db(jsonrpc, dbs, n_dbs, &remotes); | |
f85f8ebb | 157 | |
aa78de9d | 158 | retval = unixctl_server_create(unixctl_path, &unixctl); |
f85f8ebb | 159 | if (retval) { |
4d12270a | 160 | exit(EXIT_FAILURE); |
f85f8ebb BP |
161 | } |
162 | ||
475afa1b BP |
163 | if (run_command) { |
164 | char *run_argv[4]; | |
165 | ||
166 | run_argv[0] = "/bin/sh"; | |
167 | run_argv[1] = "-c"; | |
168 | run_argv[2] = run_command; | |
169 | run_argv[3] = NULL; | |
170 | ||
171 | retval = process_start(run_argv, NULL, 0, NULL, 0, &run_process); | |
172 | if (retval) { | |
173 | ovs_fatal(retval, "%s: process failed to start", run_command); | |
174 | } | |
175 | } else { | |
176 | run_process = NULL; | |
177 | } | |
178 | ||
95440284 | 179 | daemonize_complete(); |
aa78de9d | 180 | |
08b9b190 EJ |
181 | if (!run_command) { |
182 | /* ovsdb-server is usually a long-running process, in which case it | |
183 | * makes plenty of sense to log the version, but --run makes | |
184 | * ovsdb-server more like a command-line tool, so skip it. */ | |
185 | VLOG_INFO("%s (Open vSwitch) %s", program_name, VERSION); | |
186 | } | |
9dbc190c | 187 | |
0e15264f | 188 | unixctl_command_register("exit", "", 0, 0, ovsdb_server_exit, &exiting); |
b4e8d170 BP |
189 | unixctl_command_register("ovsdb-server/compact", "", 0, 1, |
190 | ovsdb_server_compact, dbs); | |
0e15264f | 191 | unixctl_command_register("ovsdb-server/reconnect", "", 0, 0, |
7ff2009a | 192 | ovsdb_server_reconnect, jsonrpc); |
aa78de9d | 193 | |
b421d2af BP |
194 | add_remote_aux.remotes = &remotes; |
195 | add_remote_aux.dbs = dbs; | |
196 | add_remote_aux.n_dbs = n_dbs; | |
197 | unixctl_command_register("ovsdb-server/add-remote", "REMOTE", 1, 1, | |
198 | ovsdb_server_add_remote, &add_remote_aux); | |
199 | unixctl_command_register("ovsdb-server/remove-remote", "REMOTE", 1, 1, | |
200 | ovsdb_server_remove_remote, &remotes); | |
201 | unixctl_command_register("ovsdb-server/list-remotes", "", 0, 0, | |
202 | ovsdb_server_list_remotes, &remotes); | |
203 | ||
aa78de9d BP |
204 | exiting = false; |
205 | while (!exiting) { | |
b4e8d170 BP |
206 | int i; |
207 | ||
0d085684 BP |
208 | memory_run(); |
209 | if (memory_should_report()) { | |
210 | struct simap usage; | |
211 | ||
212 | simap_init(&usage); | |
213 | ovsdb_jsonrpc_server_get_memory_usage(jsonrpc, &usage); | |
b4e8d170 BP |
214 | for (i = 0; i < n_dbs; i++) { |
215 | ovsdb_get_memory_usage(dbs[i].db, &usage); | |
216 | } | |
0d085684 BP |
217 | memory_report(&usage); |
218 | simap_destroy(&usage); | |
219 | } | |
220 | ||
b421d2af BP |
221 | /* Run unixctl_server_run() before reconfigure_from_db() because |
222 | * ovsdb-server/add-remote and ovsdb-server/remove-remote can change | |
223 | * the set of remotes that reconfigure_from_db() uses. */ | |
224 | unixctl_server_run(unixctl); | |
225 | ||
b4e8d170 | 226 | reconfigure_from_db(jsonrpc, dbs, n_dbs, &remotes); |
f85f8ebb | 227 | ovsdb_jsonrpc_server_run(jsonrpc); |
b4e8d170 BP |
228 | |
229 | for (i = 0; i < n_dbs; i++) { | |
230 | ovsdb_trigger_run(dbs[i].db, time_msec()); | |
231 | } | |
475afa1b BP |
232 | if (run_process && process_exited(run_process)) { |
233 | exiting = true; | |
234 | } | |
f85f8ebb | 235 | |
0b3e7a8b AE |
236 | /* update Manager status(es) every 5 seconds */ |
237 | if (time_msec() >= status_timer) { | |
238 | status_timer = time_msec() + 5000; | |
b4e8d170 | 239 | update_remote_status(jsonrpc, &remotes, dbs, n_dbs); |
0b3e7a8b AE |
240 | } |
241 | ||
0d085684 | 242 | memory_wait(); |
f85f8ebb BP |
243 | ovsdb_jsonrpc_server_wait(jsonrpc); |
244 | unixctl_server_wait(unixctl); | |
b4e8d170 BP |
245 | for (i = 0; i < n_dbs; i++) { |
246 | ovsdb_trigger_wait(dbs[i].db, time_msec()); | |
247 | } | |
475afa1b BP |
248 | if (run_process) { |
249 | process_wait(run_process); | |
250 | } | |
6d37aaf1 BP |
251 | if (exiting) { |
252 | poll_immediate_wake(); | |
253 | } | |
0b3e7a8b | 254 | poll_timer_wait_until(status_timer); |
f85f8ebb BP |
255 | poll_block(); |
256 | } | |
23935e8b | 257 | ovsdb_jsonrpc_server_destroy(jsonrpc); |
b4e8d170 BP |
258 | for (i = 0; i < n_dbs; i++) { |
259 | ovsdb_destroy(dbs[i].db); | |
260 | } | |
b3c01ed3 | 261 | sset_destroy(&remotes); |
23935e8b | 262 | unixctl_server_destroy(unixctl); |
f85f8ebb | 263 | |
475afa1b BP |
264 | if (run_process && process_exited(run_process)) { |
265 | int status = process_status(run_process); | |
266 | if (status) { | |
267 | ovs_fatal(0, "%s: child exited, %s", | |
268 | run_command, process_status_msg(status)); | |
269 | } | |
270 | } | |
271 | ||
f85f8ebb BP |
272 | return 0; |
273 | } | |
274 | ||
b4e8d170 BP |
275 | static const struct db * |
276 | find_db(const struct db dbs[], size_t n_dbs, const char *db_name) | |
277 | { | |
278 | size_t i; | |
279 | ||
280 | for (i = 0; i < n_dbs; i++) { | |
281 | if (!strcmp(dbs[i].db->schema->name, db_name)) { | |
282 | return &dbs[i]; | |
283 | } | |
284 | } | |
285 | ||
286 | return NULL; | |
287 | } | |
288 | ||
c02cf07b BP |
289 | static char * WARN_UNUSED_RESULT |
290 | parse_db_column__(const struct db dbs[], size_t n_dbs, | |
291 | const char *name_, char *name, | |
292 | const struct db **dbp, | |
293 | const struct ovsdb_table **tablep, | |
294 | const struct ovsdb_column **columnp) | |
0b1fae1b | 295 | { |
b4e8d170 | 296 | const char *table_name, *column_name; |
0b1fae1b BP |
297 | const struct ovsdb_column *column; |
298 | const struct ovsdb_table *table; | |
b4e8d170 | 299 | const char *tokens[3]; |
0b1fae1b | 300 | char *save_ptr = NULL; |
b4e8d170 | 301 | const struct db *db; |
0b1fae1b | 302 | |
c02cf07b BP |
303 | *dbp = NULL; |
304 | *tablep = NULL; | |
305 | *columnp = NULL; | |
306 | ||
b0ef0551 | 307 | strtok_r(name, ":", &save_ptr); /* "db:" */ |
b4e8d170 BP |
308 | tokens[0] = strtok_r(NULL, ",", &save_ptr); |
309 | tokens[1] = strtok_r(NULL, ",", &save_ptr); | |
310 | tokens[2] = strtok_r(NULL, ",", &save_ptr); | |
311 | if (!tokens[0] || !tokens[1]) { | |
c02cf07b | 312 | return xasprintf("\"%s\": invalid syntax", name_); |
0b1fae1b | 313 | } |
b4e8d170 BP |
314 | if (tokens[2]) { |
315 | const char *db_name = tokens[0]; | |
316 | table_name = tokens[1]; | |
317 | column_name = tokens[2]; | |
318 | ||
319 | db = find_db(dbs, n_dbs, tokens[0]); | |
320 | if (!db) { | |
c02cf07b | 321 | return xasprintf("\"%s\": no database named %s", name_, db_name); |
b4e8d170 BP |
322 | } |
323 | } else { | |
324 | if (n_dbs > 1) { | |
c02cf07b BP |
325 | return xasprintf("\"%s\": database name must be specified " |
326 | "(because multiple databases are configured)", | |
327 | name_); | |
b4e8d170 BP |
328 | } |
329 | ||
330 | table_name = tokens[0]; | |
331 | column_name = tokens[1]; | |
332 | db = &dbs[0]; | |
333 | } | |
0b1fae1b | 334 | |
b4e8d170 | 335 | table = ovsdb_get_table(db->db, table_name); |
0b1fae1b | 336 | if (!table) { |
c02cf07b | 337 | return xasprintf("\"%s\": no table named %s", name_, table_name); |
0b1fae1b BP |
338 | } |
339 | ||
340 | column = ovsdb_table_schema_get_column(table->schema, column_name); | |
341 | if (!column) { | |
c02cf07b BP |
342 | return xasprintf("\"%s\": table \"%s\" has no column \"%s\"", |
343 | name_, table_name, column_name); | |
0b1fae1b BP |
344 | } |
345 | ||
b4e8d170 | 346 | *dbp = db; |
94db5407 BP |
347 | *columnp = column; |
348 | *tablep = table; | |
c02cf07b | 349 | return NULL; |
94db5407 BP |
350 | } |
351 | ||
c02cf07b BP |
352 | /* Returns NULL if successful, otherwise a malloc()'d string describing the |
353 | * error. */ | |
354 | static char * WARN_UNUSED_RESULT | |
355 | parse_db_column(const struct db dbs[], size_t n_dbs, | |
356 | const char *name_, | |
357 | const struct db **dbp, | |
358 | const struct ovsdb_table **tablep, | |
359 | const struct ovsdb_column **columnp) | |
360 | { | |
361 | char *name = xstrdup(name_); | |
362 | char *retval = parse_db_column__(dbs, n_dbs, name_, name, | |
363 | dbp, tablep, columnp); | |
364 | free(name); | |
365 | return retval; | |
366 | } | |
367 | ||
368 | /* Returns NULL if successful, otherwise a malloc()'d string describing the | |
369 | * error. */ | |
370 | static char * WARN_UNUSED_RESULT | |
b4e8d170 | 371 | parse_db_string_column(const struct db dbs[], size_t n_dbs, |
94db5407 | 372 | const char *name, |
b4e8d170 | 373 | const struct db **dbp, |
94db5407 BP |
374 | const struct ovsdb_table **tablep, |
375 | const struct ovsdb_column **columnp) | |
376 | { | |
c02cf07b | 377 | char *retval; |
94db5407 | 378 | |
c02cf07b BP |
379 | retval = parse_db_column(dbs, n_dbs, name, dbp, tablep, columnp); |
380 | if (retval) { | |
381 | return retval; | |
382 | } | |
94db5407 | 383 | |
c02cf07b BP |
384 | if ((*columnp)->type.key.type != OVSDB_TYPE_STRING |
385 | || (*columnp)->type.value.type != OVSDB_TYPE_VOID) { | |
386 | return xasprintf("\"%s\": table \"%s\" column \"%s\" is " | |
387 | "not string or set of strings", | |
388 | name, (*tablep)->schema->name, (*columnp)->name); | |
78876719 BP |
389 | } |
390 | ||
c02cf07b | 391 | return NULL; |
78876719 BP |
392 | } |
393 | ||
a02f8286 | 394 | static OVS_UNUSED const char * |
b4e8d170 | 395 | query_db_string(const struct db dbs[], size_t n_dbs, const char *name) |
78876719 BP |
396 | { |
397 | if (!name || strncmp(name, "db:", 3)) { | |
398 | return name; | |
399 | } else { | |
400 | const struct ovsdb_column *column; | |
401 | const struct ovsdb_table *table; | |
402 | const struct ovsdb_row *row; | |
b4e8d170 | 403 | const struct db *db; |
c02cf07b | 404 | char *retval; |
78876719 | 405 | |
c02cf07b BP |
406 | retval = parse_db_string_column(dbs, n_dbs, name, |
407 | &db, &table, &column); | |
408 | if (retval) { | |
409 | ovs_fatal(0, "%s", retval); | |
410 | } | |
78876719 | 411 | |
4e8e4213 | 412 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { |
78876719 BP |
413 | const struct ovsdb_datum *datum; |
414 | size_t i; | |
415 | ||
416 | datum = &row->fields[column->index]; | |
417 | for (i = 0; i < datum->n; i++) { | |
418 | if (datum->keys[i].string[0]) { | |
419 | return datum->keys[i].string; | |
420 | } | |
421 | } | |
422 | } | |
423 | return NULL; | |
0b1fae1b | 424 | } |
78876719 BP |
425 | } |
426 | ||
94db5407 BP |
427 | static struct ovsdb_jsonrpc_options * |
428 | add_remote(struct shash *remotes, const char *target) | |
429 | { | |
430 | struct ovsdb_jsonrpc_options *options; | |
431 | ||
432 | options = shash_find_data(remotes, target); | |
433 | if (!options) { | |
f1936eb6 | 434 | options = ovsdb_jsonrpc_default_options(target); |
94db5407 BP |
435 | shash_add(remotes, target, options); |
436 | } | |
437 | ||
438 | return options; | |
439 | } | |
440 | ||
0b3e7a8b AE |
441 | static struct ovsdb_datum * |
442 | get_datum(struct ovsdb_row *row, const char *column_name, | |
443 | const enum ovsdb_atomic_type key_type, | |
444 | const enum ovsdb_atomic_type value_type, | |
445 | const size_t n_max) | |
94db5407 BP |
446 | { |
447 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
448 | const struct ovsdb_table_schema *schema = row->table->schema; | |
449 | const struct ovsdb_column *column; | |
94db5407 BP |
450 | |
451 | column = ovsdb_table_schema_get_column(schema, column_name); | |
452 | if (!column) { | |
453 | VLOG_DBG_RL(&rl, "Table `%s' has no `%s' column", | |
454 | schema->name, column_name); | |
0b3e7a8b | 455 | return NULL; |
94db5407 BP |
456 | } |
457 | ||
0b3e7a8b AE |
458 | if (column->type.key.type != key_type |
459 | || column->type.value.type != value_type | |
460 | || column->type.n_max != n_max) { | |
94db5407 BP |
461 | if (!VLOG_DROP_DBG(&rl)) { |
462 | char *type_name = ovsdb_type_to_english(&column->type); | |
463 | VLOG_DBG("Table `%s' column `%s' has type %s, not expected " | |
0b3e7a8b AE |
464 | "key type %s, value type %s, max elements %zd.", |
465 | schema->name, column_name, type_name, | |
466 | ovsdb_atomic_type_to_string(key_type), | |
467 | ovsdb_atomic_type_to_string(value_type), | |
468 | n_max); | |
469 | free(type_name); | |
94db5407 | 470 | } |
0b3e7a8b | 471 | return NULL; |
94db5407 BP |
472 | } |
473 | ||
0b3e7a8b AE |
474 | return &row->fields[column->index]; |
475 | } | |
476 | ||
cea15768 EJ |
477 | /* Read string-string key-values from a map. Returns the value associated with |
478 | * 'key', if found, or NULL */ | |
479 | static const char * | |
f125905c | 480 | read_map_string_column(const struct ovsdb_row *row, const char *column_name, |
cea15768 | 481 | const char *key) |
f125905c MM |
482 | { |
483 | const struct ovsdb_datum *datum; | |
484 | union ovsdb_atom *atom_key = NULL, *atom_value = NULL; | |
485 | size_t i; | |
486 | ||
ebc56baa BP |
487 | datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name, |
488 | OVSDB_TYPE_STRING, OVSDB_TYPE_STRING, UINT_MAX); | |
f125905c MM |
489 | |
490 | if (!datum) { | |
cea15768 | 491 | return NULL; |
f125905c MM |
492 | } |
493 | ||
494 | for (i = 0; i < datum->n; i++) { | |
495 | atom_key = &datum->keys[i]; | |
496 | if (!strcmp(atom_key->string, key)){ | |
497 | atom_value = &datum->values[i]; | |
498 | break; | |
499 | } | |
500 | } | |
501 | ||
cea15768 | 502 | return atom_value ? atom_value->string : NULL; |
f125905c MM |
503 | } |
504 | ||
0b3e7a8b AE |
505 | static const union ovsdb_atom * |
506 | read_column(const struct ovsdb_row *row, const char *column_name, | |
507 | enum ovsdb_atomic_type type) | |
508 | { | |
509 | const struct ovsdb_datum *datum; | |
510 | ||
ebc56baa BP |
511 | datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name, type, |
512 | OVSDB_TYPE_VOID, 1); | |
0b3e7a8b | 513 | return datum && datum->n ? datum->keys : NULL; |
94db5407 BP |
514 | } |
515 | ||
516 | static bool | |
517 | read_integer_column(const struct ovsdb_row *row, const char *column_name, | |
518 | long long int *integerp) | |
519 | { | |
520 | const union ovsdb_atom *atom; | |
521 | ||
522 | atom = read_column(row, column_name, OVSDB_TYPE_INTEGER); | |
523 | *integerp = atom ? atom->integer : 0; | |
524 | return atom != NULL; | |
525 | } | |
526 | ||
527 | static bool | |
528 | read_string_column(const struct ovsdb_row *row, const char *column_name, | |
529 | const char **stringp) | |
530 | { | |
531 | const union ovsdb_atom *atom; | |
532 | ||
533 | atom = read_column(row, column_name, OVSDB_TYPE_STRING); | |
e3c17733 | 534 | *stringp = atom ? atom->string : NULL; |
94db5407 BP |
535 | return atom != NULL; |
536 | } | |
537 | ||
0b3e7a8b AE |
538 | static void |
539 | write_bool_column(struct ovsdb_row *row, const char *column_name, bool value) | |
540 | { | |
c824c8a3 BP |
541 | const struct ovsdb_column *column; |
542 | struct ovsdb_datum *datum; | |
0b3e7a8b | 543 | |
c824c8a3 BP |
544 | column = ovsdb_table_schema_get_column(row->table->schema, column_name); |
545 | datum = get_datum(row, column_name, OVSDB_TYPE_BOOLEAN, | |
546 | OVSDB_TYPE_VOID, 1); | |
0b3e7a8b AE |
547 | if (!datum) { |
548 | return; | |
549 | } | |
c824c8a3 BP |
550 | |
551 | if (datum->n != 1) { | |
552 | ovsdb_datum_destroy(datum, &column->type); | |
553 | ||
554 | datum->n = 1; | |
555 | datum->keys = xmalloc(sizeof *datum->keys); | |
556 | datum->values = NULL; | |
557 | } | |
558 | ||
0b3e7a8b AE |
559 | datum->keys[0].boolean = value; |
560 | } | |
561 | ||
562 | static void | |
563 | write_string_string_column(struct ovsdb_row *row, const char *column_name, | |
564 | char **keys, char **values, size_t n) | |
565 | { | |
566 | const struct ovsdb_column *column; | |
567 | struct ovsdb_datum *datum; | |
568 | size_t i; | |
569 | ||
570 | column = ovsdb_table_schema_get_column(row->table->schema, column_name); | |
571 | datum = get_datum(row, column_name, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING, | |
572 | UINT_MAX); | |
573 | if (!datum) { | |
bfe834eb BP |
574 | for (i = 0; i < n; i++) { |
575 | free(keys[i]); | |
576 | free(values[i]); | |
577 | } | |
0b3e7a8b AE |
578 | return; |
579 | } | |
580 | ||
581 | /* Free existing data. */ | |
582 | ovsdb_datum_destroy(datum, &column->type); | |
583 | ||
584 | /* Allocate space for new values. */ | |
585 | datum->n = n; | |
586 | datum->keys = xmalloc(n * sizeof *datum->keys); | |
587 | datum->values = xmalloc(n * sizeof *datum->values); | |
588 | ||
589 | for (i = 0; i < n; ++i) { | |
590 | datum->keys[i].string = keys[i]; | |
591 | datum->values[i].string = values[i]; | |
592 | } | |
593 | ||
594 | /* Sort and check constraints. */ | |
595 | ovsdb_datum_sort_assert(datum, column->type.key.type); | |
596 | } | |
597 | ||
94db5407 BP |
598 | /* Adds a remote and options to 'remotes', based on the Manager table row in |
599 | * 'row'. */ | |
600 | static void | |
601 | add_manager_options(struct shash *remotes, const struct ovsdb_row *row) | |
602 | { | |
603 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
604 | struct ovsdb_jsonrpc_options *options; | |
605 | long long int max_backoff, probe_interval; | |
cea15768 | 606 | const char *target, *dscp_string; |
94db5407 BP |
607 | |
608 | if (!read_string_column(row, "target", &target) || !target) { | |
609 | VLOG_INFO_RL(&rl, "Table `%s' has missing or invalid `target' column", | |
610 | row->table->schema->name); | |
611 | return; | |
612 | } | |
613 | ||
614 | options = add_remote(remotes, target); | |
615 | if (read_integer_column(row, "max_backoff", &max_backoff)) { | |
616 | options->max_backoff = max_backoff; | |
617 | } | |
f441c1a8 | 618 | if (read_integer_column(row, "inactivity_probe", &probe_interval)) { |
94db5407 BP |
619 | options->probe_interval = probe_interval; |
620 | } | |
f125905c | 621 | |
cea15768 EJ |
622 | options->dscp = DSCP_DEFAULT; |
623 | dscp_string = read_map_string_column(row, "other_config", "dscp"); | |
624 | if (dscp_string) { | |
625 | int dscp = atoi(dscp_string); | |
626 | if (dscp >= 0 && dscp <= 63) { | |
627 | options->dscp = dscp; | |
628 | } | |
629 | } | |
94db5407 BP |
630 | } |
631 | ||
78876719 | 632 | static void |
b4e8d170 | 633 | query_db_remotes(const char *name, const struct db dbs[], size_t n_dbs, |
78876719 BP |
634 | struct shash *remotes) |
635 | { | |
636 | const struct ovsdb_column *column; | |
637 | const struct ovsdb_table *table; | |
638 | const struct ovsdb_row *row; | |
b4e8d170 | 639 | const struct db *db; |
c02cf07b | 640 | char *retval; |
78876719 | 641 | |
c02cf07b BP |
642 | retval = parse_db_column(dbs, n_dbs, name, &db, &table, &column); |
643 | if (retval) { | |
644 | ovs_fatal(0, "%s", retval); | |
645 | } | |
0b1fae1b | 646 | |
94db5407 BP |
647 | if (column->type.key.type == OVSDB_TYPE_STRING |
648 | && column->type.value.type == OVSDB_TYPE_VOID) { | |
649 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { | |
650 | const struct ovsdb_datum *datum; | |
651 | size_t i; | |
652 | ||
653 | datum = &row->fields[column->index]; | |
654 | for (i = 0; i < datum->n; i++) { | |
655 | add_remote(remotes, datum->keys[i].string); | |
656 | } | |
657 | } | |
658 | } else if (column->type.key.type == OVSDB_TYPE_UUID | |
659 | && column->type.key.u.uuid.refTable | |
660 | && column->type.value.type == OVSDB_TYPE_VOID) { | |
661 | const struct ovsdb_table *ref_table = column->type.key.u.uuid.refTable; | |
662 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { | |
663 | const struct ovsdb_datum *datum; | |
664 | size_t i; | |
665 | ||
666 | datum = &row->fields[column->index]; | |
667 | for (i = 0; i < datum->n; i++) { | |
668 | const struct ovsdb_row *ref_row; | |
0b1fae1b | 669 | |
94db5407 BP |
670 | ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid); |
671 | if (ref_row) { | |
672 | add_manager_options(remotes, ref_row); | |
673 | } | |
674 | } | |
0b1fae1b BP |
675 | } |
676 | } | |
677 | } | |
678 | ||
0b3e7a8b AE |
679 | static void |
680 | update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn, | |
87fcbc60 | 681 | const struct ovsdb_jsonrpc_server *jsonrpc) |
0b3e7a8b | 682 | { |
87fcbc60 | 683 | struct ovsdb_jsonrpc_remote_status status; |
0b3e7a8b AE |
684 | struct ovsdb_row *rw_row; |
685 | const char *target; | |
87fcbc60 | 686 | char *keys[8], *values[8]; |
0b3e7a8b AE |
687 | size_t n = 0; |
688 | ||
689 | /* Get the "target" (protocol/host/port) spec. */ | |
690 | if (!read_string_column(row, "target", &target)) { | |
691 | /* Bad remote spec or incorrect schema. */ | |
692 | return; | |
693 | } | |
0b3e7a8b | 694 | rw_row = ovsdb_txn_row_modify(txn, row); |
87fcbc60 | 695 | ovsdb_jsonrpc_server_get_remote_status(jsonrpc, target, &status); |
0b3e7a8b AE |
696 | |
697 | /* Update status information columns. */ | |
87fcbc60 | 698 | write_bool_column(rw_row, "is_connected", status.is_connected); |
0b3e7a8b | 699 | |
87fcbc60 BP |
700 | if (status.state) { |
701 | keys[n] = xstrdup("state"); | |
702 | values[n++] = xstrdup(status.state); | |
703 | } | |
704 | if (status.sec_since_connect != UINT_MAX) { | |
5eda645e | 705 | keys[n] = xstrdup("sec_since_connect"); |
87fcbc60 | 706 | values[n++] = xasprintf("%u", status.sec_since_connect); |
5eda645e | 707 | } |
87fcbc60 | 708 | if (status.sec_since_disconnect != UINT_MAX) { |
5eda645e | 709 | keys[n] = xstrdup("sec_since_disconnect"); |
87fcbc60 | 710 | values[n++] = xasprintf("%u", status.sec_since_disconnect); |
5eda645e | 711 | } |
87fcbc60 | 712 | if (status.last_error) { |
0b3e7a8b AE |
713 | keys[n] = xstrdup("last_error"); |
714 | values[n++] = | |
87fcbc60 | 715 | xstrdup(ovs_retval_to_string(status.last_error)); |
0b3e7a8b | 716 | } |
da897f41 BP |
717 | if (status.locks_held && status.locks_held[0]) { |
718 | keys[n] = xstrdup("locks_held"); | |
719 | values[n++] = xstrdup(status.locks_held); | |
720 | } | |
721 | if (status.locks_waiting && status.locks_waiting[0]) { | |
722 | keys[n] = xstrdup("locks_waiting"); | |
723 | values[n++] = xstrdup(status.locks_waiting); | |
724 | } | |
725 | if (status.locks_lost && status.locks_lost[0]) { | |
726 | keys[n] = xstrdup("locks_lost"); | |
727 | values[n++] = xstrdup(status.locks_lost); | |
728 | } | |
a11f6164 BP |
729 | if (status.n_connections > 1) { |
730 | keys[n] = xstrdup("n_connections"); | |
731 | values[n++] = xasprintf("%d", status.n_connections); | |
732 | } | |
0b3e7a8b | 733 | write_string_string_column(rw_row, "status", keys, values, n); |
da897f41 BP |
734 | |
735 | ovsdb_jsonrpc_server_free_remote_status(&status); | |
0b3e7a8b AE |
736 | } |
737 | ||
738 | static void | |
b4e8d170 | 739 | update_remote_rows(const struct db dbs[], size_t n_dbs, |
87fcbc60 BP |
740 | const char *remote_name, |
741 | const struct ovsdb_jsonrpc_server *jsonrpc) | |
0b3e7a8b AE |
742 | { |
743 | const struct ovsdb_table *table, *ref_table; | |
744 | const struct ovsdb_column *column; | |
745 | const struct ovsdb_row *row; | |
b4e8d170 | 746 | const struct db *db; |
c02cf07b | 747 | char *retval; |
0b3e7a8b AE |
748 | |
749 | if (strncmp("db:", remote_name, 3)) { | |
750 | return; | |
751 | } | |
752 | ||
c02cf07b BP |
753 | retval = parse_db_column(dbs, n_dbs, remote_name, &db, &table, &column); |
754 | if (retval) { | |
755 | ovs_fatal(0, "%s", retval); | |
756 | } | |
0b3e7a8b AE |
757 | |
758 | if (column->type.key.type != OVSDB_TYPE_UUID | |
759 | || !column->type.key.u.uuid.refTable | |
760 | || column->type.value.type != OVSDB_TYPE_VOID) { | |
761 | return; | |
762 | } | |
763 | ||
764 | ref_table = column->type.key.u.uuid.refTable; | |
765 | ||
766 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { | |
767 | const struct ovsdb_datum *datum; | |
768 | size_t i; | |
769 | ||
770 | datum = &row->fields[column->index]; | |
771 | for (i = 0; i < datum->n; i++) { | |
772 | const struct ovsdb_row *ref_row; | |
773 | ||
774 | ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid); | |
775 | if (ref_row) { | |
b4e8d170 | 776 | update_remote_row(ref_row, db->txn, jsonrpc); |
0b3e7a8b AE |
777 | } |
778 | } | |
779 | } | |
780 | } | |
781 | ||
782 | static void | |
783 | update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc, | |
b4e8d170 BP |
784 | const struct sset *remotes, |
785 | struct db dbs[], size_t n_dbs) | |
0b3e7a8b AE |
786 | { |
787 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
b3c01ed3 | 788 | const char *remote; |
b4e8d170 | 789 | size_t i; |
0b3e7a8b | 790 | |
b4e8d170 BP |
791 | for (i = 0; i < n_dbs; i++) { |
792 | dbs[i].txn = ovsdb_txn_create(dbs[i].db); | |
793 | } | |
0b3e7a8b AE |
794 | |
795 | /* Iterate over --remote arguments given on command line. */ | |
b3c01ed3 | 796 | SSET_FOR_EACH (remote, remotes) { |
b4e8d170 | 797 | update_remote_rows(dbs, n_dbs, remote, jsonrpc); |
0b3e7a8b AE |
798 | } |
799 | ||
b4e8d170 BP |
800 | for (i = 0; i < n_dbs; i++) { |
801 | struct ovsdb_error *error = ovsdb_txn_commit(dbs[i].txn, false); | |
802 | if (error) { | |
803 | VLOG_ERR_RL(&rl, "Failed to update remote status: %s", | |
804 | ovsdb_error_to_string(error)); | |
805 | ovsdb_error_destroy(error); | |
806 | } | |
0b3e7a8b | 807 | } |
0b3e7a8b AE |
808 | } |
809 | ||
78876719 | 810 | /* Reconfigures ovsdb-server based on information in the database. */ |
0b1fae1b | 811 | static void |
78876719 | 812 | reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc, |
b4e8d170 | 813 | const struct db dbs[], size_t n_dbs, struct sset *remotes) |
0b1fae1b BP |
814 | { |
815 | struct shash resolved_remotes; | |
b3c01ed3 | 816 | const char *name; |
0b1fae1b | 817 | |
78876719 | 818 | /* Configure remotes. */ |
0b1fae1b | 819 | shash_init(&resolved_remotes); |
b3c01ed3 | 820 | SSET_FOR_EACH (name, remotes) { |
0b1fae1b | 821 | if (!strncmp(name, "db:", 3)) { |
b4e8d170 | 822 | query_db_remotes(name, dbs, n_dbs, &resolved_remotes); |
0b1fae1b | 823 | } else { |
94db5407 | 824 | add_remote(&resolved_remotes, name); |
0b1fae1b BP |
825 | } |
826 | } | |
827 | ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes); | |
88b64974 | 828 | shash_destroy_free_data(&resolved_remotes); |
0b1fae1b | 829 | |
78876719 | 830 | /* Configure SSL. */ |
b4e8d170 BP |
831 | stream_ssl_set_key_and_cert(query_db_string(dbs, n_dbs, private_key_file), |
832 | query_db_string(dbs, n_dbs, certificate_file)); | |
833 | stream_ssl_set_ca_cert_file(query_db_string(dbs, n_dbs, ca_cert_file), | |
78876719 BP |
834 | bootstrap_ca_cert); |
835 | } | |
0b1fae1b | 836 | |
aa78de9d | 837 | static void |
0e15264f BP |
838 | ovsdb_server_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, |
839 | const char *argv[] OVS_UNUSED, | |
aa78de9d BP |
840 | void *exiting_) |
841 | { | |
842 | bool *exiting = exiting_; | |
843 | *exiting = true; | |
bde9f75d | 844 | unixctl_command_reply(conn, NULL); |
aa78de9d BP |
845 | } |
846 | ||
ada496b5 | 847 | static void |
b4e8d170 BP |
848 | ovsdb_server_compact(struct unixctl_conn *conn, int argc, |
849 | const char *argv[], void *dbs_) | |
ada496b5 | 850 | { |
b4e8d170 BP |
851 | struct db *dbs = dbs_; |
852 | struct ds reply; | |
853 | struct db *db; | |
854 | int n = 0; | |
ada496b5 | 855 | |
b4e8d170 BP |
856 | ds_init(&reply); |
857 | for (db = dbs; db->filename != NULL; db++) { | |
858 | const char *name = db->db->schema->name; | |
859 | ||
860 | if (argc < 2 || !strcmp(argv[1], name)) { | |
861 | struct ovsdb_error *error; | |
862 | ||
863 | VLOG_INFO("compacting %s database by user request", name); | |
864 | ||
865 | error = ovsdb_file_compact(db->file); | |
866 | if (error) { | |
867 | char *s = ovsdb_error_to_string(error); | |
868 | ds_put_format(&reply, "%s\n", s); | |
869 | free(s); | |
870 | } | |
871 | ||
872 | n++; | |
873 | } | |
874 | } | |
875 | ||
876 | if (!n) { | |
877 | unixctl_command_reply_error(conn, "no database by that name"); | |
878 | } else if (reply.length) { | |
879 | unixctl_command_reply_error(conn, ds_cstr(&reply)); | |
ada496b5 | 880 | } else { |
b4e8d170 | 881 | unixctl_command_reply(conn, NULL); |
ada496b5 | 882 | } |
b4e8d170 | 883 | ds_destroy(&reply); |
ada496b5 BP |
884 | } |
885 | ||
31d0b6c9 BP |
886 | /* "ovsdb-server/reconnect": makes ovsdb-server drop all of its JSON-RPC |
887 | * connections and reconnect. */ | |
888 | static void | |
0e15264f BP |
889 | ovsdb_server_reconnect(struct unixctl_conn *conn, int argc OVS_UNUSED, |
890 | const char *argv[] OVS_UNUSED, void *jsonrpc_) | |
31d0b6c9 BP |
891 | { |
892 | struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_; | |
893 | ||
894 | ovsdb_jsonrpc_server_reconnect(jsonrpc); | |
bde9f75d | 895 | unixctl_command_reply(conn, NULL); |
31d0b6c9 BP |
896 | } |
897 | ||
b421d2af BP |
898 | /* "ovsdb-server/add-remote REMOTE": adds REMOTE to the set of remotes that |
899 | * ovsdb-server services. */ | |
900 | static void | |
901 | ovsdb_server_add_remote(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
902 | const char *argv[], void *aux_) | |
903 | { | |
904 | struct add_remote_aux *aux = aux_; | |
905 | const char *remote = argv[1]; | |
906 | ||
907 | const struct ovsdb_column *column; | |
908 | const struct ovsdb_table *table; | |
909 | const struct db *db; | |
910 | char *retval; | |
911 | ||
912 | retval = (strncmp("db:", remote, 3) | |
913 | ? NULL | |
914 | : parse_db_column(aux->dbs, aux->n_dbs, remote, | |
915 | &db, &table, &column)); | |
916 | if (!retval) { | |
917 | sset_add(aux->remotes, remote); | |
918 | unixctl_command_reply(conn, NULL); | |
919 | } else { | |
920 | unixctl_command_reply_error(conn, retval); | |
921 | free(retval); | |
922 | } | |
923 | } | |
924 | ||
925 | /* "ovsdb-server/remove-remote REMOTE": removes REMOTE frmo the set of remotes | |
926 | * that ovsdb-server services. */ | |
927 | static void | |
928 | ovsdb_server_remove_remote(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
929 | const char *argv[], void *remotes_) | |
930 | { | |
931 | struct sset *remotes = remotes_; | |
932 | struct sset_node *node; | |
933 | ||
934 | node = sset_find(remotes, argv[1]); | |
935 | if (node) { | |
936 | sset_delete(remotes, node); | |
937 | unixctl_command_reply(conn, NULL); | |
938 | } else { | |
939 | unixctl_command_reply_error(conn, "no such remote"); | |
940 | } | |
941 | } | |
942 | ||
943 | /* "ovsdb-server/list-remotes": outputs a list of configured rmeotes. */ | |
944 | static void | |
945 | ovsdb_server_list_remotes(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
946 | const char *argv[] OVS_UNUSED, void *remotes_) | |
947 | { | |
948 | struct sset *remotes = remotes_; | |
949 | const char **list, **p; | |
950 | struct ds s; | |
951 | ||
952 | ds_init(&s); | |
953 | ||
954 | list = sset_sort(remotes); | |
955 | for (p = list; *p; p++) { | |
956 | ds_put_format(&s, "%s\n", *p); | |
957 | } | |
958 | free(list); | |
959 | ||
960 | unixctl_command_reply(conn, ds_cstr(&s)); | |
961 | ds_destroy(&s); | |
962 | } | |
963 | ||
f85f8ebb | 964 | static void |
b4e8d170 BP |
965 | parse_options(int *argcp, char **argvp[], |
966 | struct sset *remotes, char **unixctl_pathp, char **run_command) | |
f85f8ebb BP |
967 | { |
968 | enum { | |
06834871 | 969 | OPT_REMOTE = UCHAR_MAX + 1, |
aa78de9d | 970 | OPT_UNIXCTL, |
475afa1b | 971 | OPT_RUN, |
9467fe62 | 972 | OPT_BOOTSTRAP_CA_CERT, |
06834871 | 973 | OPT_ENABLE_DUMMY, |
f85f8ebb | 974 | VLOG_OPTION_ENUMS, |
8274ae95 BP |
975 | LEAK_CHECKER_OPTION_ENUMS, |
976 | DAEMON_OPTION_ENUMS | |
f85f8ebb BP |
977 | }; |
978 | static struct option long_options[] = { | |
e3c17733 BP |
979 | {"remote", required_argument, NULL, OPT_REMOTE}, |
980 | {"unixctl", required_argument, NULL, OPT_UNIXCTL}, | |
981 | {"run", required_argument, NULL, OPT_RUN}, | |
982 | {"help", no_argument, NULL, 'h'}, | |
983 | {"version", no_argument, NULL, 'V'}, | |
f85f8ebb BP |
984 | DAEMON_LONG_OPTIONS, |
985 | VLOG_LONG_OPTIONS, | |
986 | LEAK_CHECKER_LONG_OPTIONS, | |
e3c17733 BP |
987 | {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, |
988 | {"private-key", required_argument, NULL, 'p'}, | |
989 | {"certificate", required_argument, NULL, 'c'}, | |
990 | {"ca-cert", required_argument, NULL, 'C'}, | |
06834871 | 991 | {"enable-dummy", optional_argument, NULL, OPT_ENABLE_DUMMY}, |
e3c17733 | 992 | {NULL, 0, NULL, 0}, |
f85f8ebb BP |
993 | }; |
994 | char *short_options = long_options_to_short_options(long_options); | |
b4e8d170 BP |
995 | int argc = *argcp; |
996 | char **argv = *argvp; | |
f85f8ebb | 997 | |
b3c01ed3 | 998 | sset_init(remotes); |
f85f8ebb BP |
999 | for (;;) { |
1000 | int c; | |
1001 | ||
1002 | c = getopt_long(argc, argv, short_options, long_options, NULL); | |
1003 | if (c == -1) { | |
1004 | break; | |
1005 | } | |
1006 | ||
1007 | switch (c) { | |
0b1fae1b | 1008 | case OPT_REMOTE: |
b3c01ed3 | 1009 | sset_add(remotes, optarg); |
f85f8ebb BP |
1010 | break; |
1011 | ||
aa78de9d BP |
1012 | case OPT_UNIXCTL: |
1013 | *unixctl_pathp = optarg; | |
1014 | break; | |
1015 | ||
475afa1b BP |
1016 | case OPT_RUN: |
1017 | *run_command = optarg; | |
1018 | break; | |
1019 | ||
f85f8ebb BP |
1020 | case 'h': |
1021 | usage(); | |
1022 | ||
1023 | case 'V': | |
55d5bb44 | 1024 | ovs_print_version(0, 0); |
f85f8ebb BP |
1025 | exit(EXIT_SUCCESS); |
1026 | ||
1027 | VLOG_OPTION_HANDLERS | |
1028 | DAEMON_OPTION_HANDLERS | |
1029 | LEAK_CHECKER_OPTION_HANDLERS | |
1030 | ||
78876719 BP |
1031 | case 'p': |
1032 | private_key_file = optarg; | |
1033 | break; | |
1034 | ||
1035 | case 'c': | |
1036 | certificate_file = optarg; | |
1037 | break; | |
1038 | ||
1039 | case 'C': | |
1040 | ca_cert_file = optarg; | |
1041 | bootstrap_ca_cert = false; | |
1042 | break; | |
9467fe62 BP |
1043 | |
1044 | case OPT_BOOTSTRAP_CA_CERT: | |
78876719 BP |
1045 | ca_cert_file = optarg; |
1046 | bootstrap_ca_cert = true; | |
9467fe62 | 1047 | break; |
9467fe62 | 1048 | |
06834871 BP |
1049 | case OPT_ENABLE_DUMMY: |
1050 | dummy_enable(optarg && !strcmp(optarg, "override")); | |
1051 | break; | |
1052 | ||
f85f8ebb BP |
1053 | case '?': |
1054 | exit(EXIT_FAILURE); | |
1055 | ||
1056 | default: | |
1057 | abort(); | |
1058 | } | |
1059 | } | |
1060 | free(short_options); | |
1061 | ||
b4e8d170 BP |
1062 | *argcp -= optind; |
1063 | *argvp += optind; | |
f85f8ebb BP |
1064 | } |
1065 | ||
1066 | static void | |
1067 | usage(void) | |
1068 | { | |
1069 | printf("%s: Open vSwitch database server\n" | |
b4e8d170 BP |
1070 | "usage: %s [OPTIONS] [DATABASE...]\n" |
1071 | "where each DATABASE is a database file in ovsdb format.\n" | |
1072 | "The default DATABASE, if none is given, is\n%s/conf.db.\n", | |
1073 | program_name, program_name, ovs_dbdir()); | |
f85f8ebb | 1074 | printf("\nJSON-RPC options (may be specified any number of times):\n" |
0b1fae1b | 1075 | " --remote=REMOTE connect or listen to REMOTE\n"); |
9467fe62 | 1076 | stream_usage("JSON-RPC", true, true, true); |
f85f8ebb BP |
1077 | daemon_usage(); |
1078 | vlog_usage(); | |
1079 | printf("\nOther options:\n" | |
475afa1b | 1080 | " --run COMMAND run COMMAND as subprocess then exit\n" |
7b38bdc8 | 1081 | " --unixctl=SOCKET override default control socket name\n" |
f85f8ebb BP |
1082 | " -h, --help display this help message\n" |
1083 | " -V, --version display version information\n"); | |
1084 | leak_checker_usage(); | |
1085 | exit(EXIT_SUCCESS); | |
1086 | } |