Branch data Line data Source code
1 : : /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc.
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 : :
18 : : #include <errno.h>
19 : : #include <getopt.h>
20 : : #include <inttypes.h>
21 : : #include <signal.h>
22 : : #include <sys/stat.h>
23 : : #include <unistd.h>
24 : :
25 : : #include "column.h"
26 : : #include "command-line.h"
27 : : #include "daemon.h"
28 : : #include "dirs.h"
29 : : #include "openvswitch/dynamic-string.h"
30 : : #include "fatal-signal.h"
31 : : #include "file.h"
32 : : #include "hash.h"
33 : : #include "openvswitch/json.h"
34 : : #include "jsonrpc.h"
35 : : #include "jsonrpc-server.h"
36 : : #include "openvswitch/list.h"
37 : : #include "memory.h"
38 : : #include "monitor.h"
39 : : #include "ovsdb.h"
40 : : #include "ovsdb-data.h"
41 : : #include "ovsdb-types.h"
42 : : #include "ovsdb-error.h"
43 : : #include "poll-loop.h"
44 : : #include "process.h"
45 : : #include "replication.h"
46 : : #include "row.h"
47 : : #include "simap.h"
48 : : #include "openvswitch/shash.h"
49 : : #include "stream-ssl.h"
50 : : #include "stream.h"
51 : : #include "sset.h"
52 : : #include "table.h"
53 : : #include "timeval.h"
54 : : #include "transaction.h"
55 : : #include "trigger.h"
56 : : #include "util.h"
57 : : #include "unixctl.h"
58 : : #include "perf-counter.h"
59 : : #include "openvswitch/vlog.h"
60 : :
61 : 2594 : VLOG_DEFINE_THIS_MODULE(ovsdb_server);
62 : :
63 : : struct db {
64 : : /* Initialized in main(). */
65 : : char *filename;
66 : : struct ovsdb_file *file;
67 : : struct ovsdb *db;
68 : :
69 : : /* Only used by update_remote_status(). */
70 : : struct ovsdb_txn *txn;
71 : : };
72 : :
73 : : /* SSL configuration. */
74 : : static char *private_key_file;
75 : : static char *certificate_file;
76 : : static char *ca_cert_file;
77 : : static bool bootstrap_ca_cert;
78 : :
79 : : static unixctl_cb_func ovsdb_server_exit;
80 : : static unixctl_cb_func ovsdb_server_compact;
81 : : static unixctl_cb_func ovsdb_server_reconnect;
82 : : static unixctl_cb_func ovsdb_server_perf_counters_clear;
83 : : static unixctl_cb_func ovsdb_server_perf_counters_show;
84 : : static unixctl_cb_func ovsdb_server_disable_monitor_cond;
85 : : static unixctl_cb_func ovsdb_server_set_active_ovsdb_server;
86 : : static unixctl_cb_func ovsdb_server_get_active_ovsdb_server;
87 : : static unixctl_cb_func ovsdb_server_connect_active_ovsdb_server;
88 : : static unixctl_cb_func ovsdb_server_disconnect_active_ovsdb_server;
89 : : static unixctl_cb_func ovsdb_server_set_sync_exclude_tables;
90 : : static unixctl_cb_func ovsdb_server_get_sync_exclude_tables;
91 : : static unixctl_cb_func ovsdb_server_get_sync_status;
92 : :
93 : : struct server_config {
94 : : struct sset *remotes;
95 : : struct shash *all_dbs;
96 : : FILE *config_tmpfile;
97 : : char **sync_from;
98 : : char **sync_exclude;
99 : : bool *is_backup;
100 : : struct ovsdb_jsonrpc_server *jsonrpc;
101 : : };
102 : : static unixctl_cb_func ovsdb_server_add_remote;
103 : : static unixctl_cb_func ovsdb_server_remove_remote;
104 : : static unixctl_cb_func ovsdb_server_list_remotes;
105 : :
106 : : static unixctl_cb_func ovsdb_server_add_database;
107 : : static unixctl_cb_func ovsdb_server_remove_database;
108 : : static unixctl_cb_func ovsdb_server_list_databases;
109 : :
110 : : static char *open_db(struct server_config *config, const char *filename);
111 : : static void close_db(struct db *db);
112 : :
113 : : static void parse_options(int *argc, char **argvp[],
114 : : struct sset *remotes, char **unixctl_pathp,
115 : : char **run_command, char **sync_from,
116 : : char **sync_exclude, bool *is_backup);
117 : : OVS_NO_RETURN static void usage(void);
118 : :
119 : : static char *reconfigure_remotes(struct ovsdb_jsonrpc_server *,
120 : : const struct shash *all_dbs,
121 : : struct sset *remotes);
122 : : static char *reconfigure_ssl(const struct shash *all_dbs);
123 : : static void report_error_if_changed(char *error, char **last_errorp);
124 : :
125 : : static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc,
126 : : const struct sset *remotes,
127 : : struct shash *all_dbs);
128 : :
129 : : static void save_config__(FILE *config_file, const struct sset *remotes,
130 : : const struct sset *db_filenames,
131 : : const char *sync_from, const char *sync_exclude,
132 : : bool is_backup);
133 : : static void save_config(struct server_config *);
134 : : static void load_config(FILE *config_file, struct sset *remotes,
135 : : struct sset *db_filenames, char **sync_from,
136 : : char **sync_exclude, bool *is_backup);
137 : :
138 : : static void
139 : 35 : ovsdb_replication_init(const char *sync_from, const char *exclude,
140 : : struct shash *all_dbs)
141 : : {
142 : 35 : replication_init(sync_from, exclude);
143 : : struct shash_node *node;
144 [ + + ][ - + ]: 70 : SHASH_FOR_EACH (node, all_dbs) {
145 : 35 : struct db *db = node->data;
146 : 35 : replication_add_local_db(db->db->schema->name, db->db);
147 : : }
148 : 35 : }
149 : :
150 : : static void
151 : 1258 : main_loop(struct ovsdb_jsonrpc_server *jsonrpc, struct shash *all_dbs,
152 : : struct unixctl_server *unixctl, struct sset *remotes,
153 : : struct process *run_process, bool *exiting, bool *is_backup)
154 : : {
155 : : char *remotes_error, *ssl_error;
156 : : struct shash_node *node;
157 : 1258 : long long int status_timer = LLONG_MIN;
158 : 1258 : bool last_role = *is_backup;
159 : :
160 : 1258 : *exiting = false;
161 : 1258 : ssl_error = NULL;
162 : 1258 : remotes_error = NULL;
163 [ + + ]: 60262 : while (!*exiting) {
164 : 59004 : memory_run();
165 [ + + ]: 59004 : if (memory_should_report()) {
166 : : struct simap usage;
167 : :
168 : 72 : simap_init(&usage);
169 : 72 : ovsdb_jsonrpc_server_get_memory_usage(jsonrpc, &usage);
170 : 72 : ovsdb_monitor_get_memory_usage(&usage);
171 [ + + ][ - + ]: 147 : SHASH_FOR_EACH(node, all_dbs) {
172 : 75 : struct db *db = node->data;
173 : 75 : ovsdb_get_memory_usage(db->db, &usage);
174 : : }
175 : 72 : memory_report(&usage);
176 : 72 : simap_destroy(&usage);
177 : : }
178 : :
179 : : /* Run unixctl_server_run() before reconfigure_remotes() because
180 : : * ovsdb-server/add-remote and ovsdb-server/remove-remote can change
181 : : * the set of remotes that reconfigure_remotes() uses. */
182 : 59004 : unixctl_server_run(unixctl);
183 : :
184 : : /* In ovsdb-server's role (active or backup) has changed, restart
185 : : * the ovsdb jsonrpc server. */
186 [ + + ]: 59004 : if (last_role != *is_backup) {
187 : 2 : bool read_only = last_role = *is_backup;
188 : 2 : ovsdb_jsonrpc_server_reconnect(jsonrpc, read_only);
189 : : }
190 : :
191 : 59004 : report_error_if_changed(
192 : : reconfigure_remotes(jsonrpc, all_dbs, remotes),
193 : : &remotes_error);
194 : 59004 : report_error_if_changed(reconfigure_ssl(all_dbs), &ssl_error);
195 : 59004 : ovsdb_jsonrpc_server_run(jsonrpc);
196 : :
197 [ + + ]: 59004 : if (*is_backup) {
198 : 542 : replication_run();
199 [ - + ]: 542 : if (!replication_is_alive()) {
200 : 0 : int retval = replication_get_last_error();
201 : 0 : ovs_fatal(retval, "replication connection failed");
202 : : }
203 : : }
204 : :
205 [ + + ][ - + ]: 121978 : SHASH_FOR_EACH(node, all_dbs) {
206 : 62974 : struct db *db = node->data;
207 : 62974 : ovsdb_trigger_run(db->db, time_msec());
208 : : }
209 [ + + ]: 59004 : if (run_process) {
210 : 723 : process_run();
211 [ + + ]: 723 : if (process_exited(run_process)) {
212 : 108 : *exiting = true;
213 : : }
214 : : }
215 : :
216 : : /* update Manager status(es) every 2.5 seconds */
217 [ + + ]: 59004 : if (time_msec() >= status_timer) {
218 : 2679 : status_timer = time_msec() + 2500;
219 : 2679 : update_remote_status(jsonrpc, remotes, all_dbs);
220 : : }
221 : :
222 : 59004 : memory_wait();
223 [ + + ]: 59004 : if (*is_backup) {
224 : 542 : replication_wait();
225 : : }
226 : :
227 : 59004 : ovsdb_jsonrpc_server_wait(jsonrpc);
228 : 59004 : unixctl_server_wait(unixctl);
229 [ + + ][ - + ]: 121978 : SHASH_FOR_EACH(node, all_dbs) {
230 : 62974 : struct db *db = node->data;
231 : 62974 : ovsdb_trigger_wait(db->db, time_msec());
232 : : }
233 [ + + ]: 59004 : if (run_process) {
234 : 723 : process_wait(run_process);
235 : : }
236 [ + + ]: 59004 : if (*exiting) {
237 : 1258 : poll_immediate_wake();
238 : : }
239 : 59004 : poll_timer_wait_until(status_timer);
240 : 59004 : poll_block();
241 [ - + ]: 59004 : if (should_service_stop()) {
242 : 0 : *exiting = true;
243 : : }
244 : : }
245 : :
246 : 1258 : free(remotes_error);
247 : 1258 : }
248 : :
249 : : int
250 : 1297 : main(int argc, char *argv[])
251 : : {
252 : 1297 : char *unixctl_path = NULL;
253 : 1297 : char *run_command = NULL;
254 : : struct unixctl_server *unixctl;
255 : : struct ovsdb_jsonrpc_server *jsonrpc;
256 : : struct sset remotes, db_filenames;
257 : : char *sync_from, *sync_exclude;
258 : : bool is_backup;
259 : : const char *db_filename;
260 : : struct process *run_process;
261 : : bool exiting;
262 : : int retval;
263 : : FILE *config_tmpfile;
264 : : struct server_config server_config;
265 : : struct shash all_dbs;
266 : : struct shash_node *node, *next;
267 : : char *error;
268 : : int i;
269 : :
270 : 1297 : ovs_cmdl_proctitle_init(argc, argv);
271 : 1297 : set_program_name(argv[0]);
272 : 1297 : service_start(&argc, &argv);
273 : 1297 : fatal_ignore_sigpipe();
274 : 1297 : process_init();
275 : :
276 : 1297 : bool active = false;
277 : 1297 : parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command,
278 : : &sync_from, &sync_exclude, &active);
279 [ + + ][ + + ]: 1297 : is_backup = sync_from && !active;
280 : :
281 : 1297 : daemon_become_new_user(false);
282 : :
283 : : /* Create and initialize 'config_tmpfile' as a temporary file to hold
284 : : * ovsdb-server's most basic configuration, and then save our initial
285 : : * configuration to it. When --monitor is used, this preserves the effects
286 : : * of ovs-appctl commands such as ovsdb-server/add-remote (which saves the
287 : : * new configuration) across crashes. */
288 : 1297 : config_tmpfile = tmpfile();
289 [ - + ]: 1297 : if (!config_tmpfile) {
290 : 0 : ovs_fatal(errno, "failed to create temporary file");
291 : : }
292 : :
293 : 1297 : sset_init(&db_filenames);
294 [ + + ]: 1297 : if (argc > 0) {
295 [ + + ]: 1348 : for (i = 0; i < argc; i++) {
296 : 681 : sset_add(&db_filenames, argv[i]);
297 : : }
298 : : } else {
299 : 630 : char *default_db = xasprintf("%s/conf.db", ovs_dbdir());
300 : 630 : sset_add(&db_filenames, default_db);
301 : 630 : free(default_db);
302 : : }
303 : :
304 : 1297 : server_config.remotes = &remotes;
305 : 1297 : server_config.config_tmpfile = config_tmpfile;
306 : :
307 : 1297 : save_config__(config_tmpfile, &remotes, &db_filenames, sync_from,
308 : : sync_exclude, is_backup);
309 : :
310 : 1297 : daemonize_start(false);
311 : :
312 : : /* Load the saved config. */
313 : 1260 : load_config(config_tmpfile, &remotes, &db_filenames, &sync_from,
314 : : &sync_exclude, &is_backup);
315 : :
316 : : /* Start ovsdb jsonrpc server. When running as a backup server,
317 : : * jsonrpc connections are read only. Otherwise, both read
318 : : * and write transactions are allowed. */
319 : 1260 : jsonrpc = ovsdb_jsonrpc_server_create(is_backup);
320 : :
321 : 1260 : shash_init(&all_dbs);
322 : 1260 : server_config.all_dbs = &all_dbs;
323 : 1260 : server_config.jsonrpc = jsonrpc;
324 : 1260 : server_config.sync_from = &sync_from;
325 : 1260 : server_config.sync_exclude = &sync_exclude;
326 : 1260 : server_config.is_backup = &is_backup;
327 : :
328 : 1260 : perf_counters_init();
329 : :
330 [ + - ][ + + ]: 2528 : SSET_FOR_EACH (db_filename, &db_filenames) {
[ + + ]
331 : 1268 : error = open_db(&server_config, db_filename);
332 [ - + ]: 1268 : if (error) {
333 : 0 : ovs_fatal(0, "%s", error);
334 : : }
335 : : }
336 : :
337 : 1260 : error = reconfigure_remotes(jsonrpc, &all_dbs, &remotes);
338 [ + - ]: 1260 : if (!error) {
339 : 1260 : error = reconfigure_ssl(&all_dbs);
340 : : }
341 [ - + ]: 1260 : if (error) {
342 : 0 : ovs_fatal(0, "%s", error);
343 : : }
344 : :
345 : 1260 : retval = unixctl_server_create(unixctl_path, &unixctl);
346 [ + + ]: 1260 : if (retval) {
347 : 2 : exit(EXIT_FAILURE);
348 : : }
349 : :
350 [ + + ]: 1258 : if (run_command) {
351 : : char *run_argv[4];
352 : :
353 : 108 : run_argv[0] = "/bin/sh";
354 : 108 : run_argv[1] = "-c";
355 : 108 : run_argv[2] = run_command;
356 : 108 : run_argv[3] = NULL;
357 : :
358 : 108 : retval = process_start(run_argv, &run_process);
359 [ - + ]: 108 : if (retval) {
360 : 108 : ovs_fatal(retval, "%s: process failed to start", run_command);
361 : : }
362 : : } else {
363 : 1150 : run_process = NULL;
364 : : }
365 : :
366 : 1258 : daemonize_complete();
367 : :
368 [ + + ]: 1258 : if (!run_command) {
369 : : /* ovsdb-server is usually a long-running process, in which case it
370 : : * makes plenty of sense to log the version, but --run makes
371 : : * ovsdb-server more like a command-line tool, so skip it. */
372 [ + - ]: 1150 : VLOG_INFO("%s (Open vSwitch) %s", program_name, VERSION);
373 : : }
374 : :
375 : 1258 : unixctl_command_register("exit", "", 0, 0, ovsdb_server_exit, &exiting);
376 : 1258 : unixctl_command_register("ovsdb-server/compact", "", 0, 1,
377 : : ovsdb_server_compact, &all_dbs);
378 : 1258 : unixctl_command_register("ovsdb-server/reconnect", "", 0, 0,
379 : : ovsdb_server_reconnect, jsonrpc);
380 : :
381 : 1258 : unixctl_command_register("ovsdb-server/add-remote", "REMOTE", 1, 1,
382 : : ovsdb_server_add_remote, &server_config);
383 : 1258 : unixctl_command_register("ovsdb-server/remove-remote", "REMOTE", 1, 1,
384 : : ovsdb_server_remove_remote, &server_config);
385 : 1258 : unixctl_command_register("ovsdb-server/list-remotes", "", 0, 0,
386 : : ovsdb_server_list_remotes, &remotes);
387 : :
388 : 1258 : unixctl_command_register("ovsdb-server/add-db", "DB", 1, 1,
389 : : ovsdb_server_add_database, &server_config);
390 : 1258 : unixctl_command_register("ovsdb-server/remove-db", "DB", 1, 1,
391 : : ovsdb_server_remove_database, &server_config);
392 : 1258 : unixctl_command_register("ovsdb-server/list-dbs", "", 0, 0,
393 : : ovsdb_server_list_databases, &all_dbs);
394 : 1258 : unixctl_command_register("ovsdb-server/perf-counters-show", "", 0, 0,
395 : : ovsdb_server_perf_counters_show, NULL);
396 : 1258 : unixctl_command_register("ovsdb-server/perf-counters-clear", "", 0, 0,
397 : : ovsdb_server_perf_counters_clear, NULL);
398 : 1258 : unixctl_command_register("ovsdb-server/set-active-ovsdb-server", "", 1, 1,
399 : : ovsdb_server_set_active_ovsdb_server,
400 : : &server_config);
401 : 1258 : unixctl_command_register("ovsdb-server/get-active-ovsdb-server", "", 0, 0,
402 : : ovsdb_server_get_active_ovsdb_server,
403 : : &server_config);
404 : 1258 : unixctl_command_register("ovsdb-server/connect-active-ovsdb-server", "",
405 : : 0, 0, ovsdb_server_connect_active_ovsdb_server,
406 : : &server_config);
407 : 1258 : unixctl_command_register("ovsdb-server/disconnect-active-ovsdb-server", "",
408 : : 0, 0, ovsdb_server_disconnect_active_ovsdb_server,
409 : : &server_config);
410 : 1258 : unixctl_command_register("ovsdb-server/set-sync-exclude-tables", "",
411 : : 0, 1, ovsdb_server_set_sync_exclude_tables,
412 : : &server_config);
413 : 1258 : unixctl_command_register("ovsdb-server/get-sync-exclude-tables", "",
414 : : 0, 0, ovsdb_server_get_sync_exclude_tables,
415 : : NULL);
416 : 1258 : unixctl_command_register("ovsdb-server/sync-status", "",
417 : : 0, 0, ovsdb_server_get_sync_status,
418 : : &server_config);
419 : :
420 : : /* Simulate the behavior of OVS release prior to version 2.5 that
421 : : * does not support the monitor_cond method. */
422 : 1258 : unixctl_command_register("ovsdb-server/disable-monitor-cond", "", 0, 0,
423 : : ovsdb_server_disable_monitor_cond, jsonrpc);
424 : :
425 [ + + ]: 1258 : if (is_backup) {
426 : 33 : ovsdb_replication_init(sync_from, sync_exclude, &all_dbs);
427 : : }
428 : :
429 : 1258 : main_loop(jsonrpc, &all_dbs, unixctl, &remotes, run_process, &exiting,
430 : : &is_backup);
431 : :
432 : 1258 : ovsdb_jsonrpc_server_destroy(jsonrpc);
433 [ + + ][ - + ]: 2525 : SHASH_FOR_EACH_SAFE(node, next, &all_dbs) {
[ + + ]
434 : 1267 : struct db *db = node->data;
435 : 1267 : close_db(db);
436 : 1267 : shash_delete(&all_dbs, node);
437 : : }
438 : 1258 : shash_destroy(&all_dbs);
439 : 1258 : sset_destroy(&remotes);
440 : 1258 : sset_destroy(&db_filenames);
441 : 1258 : free(sync_from);
442 : 1258 : free(sync_exclude);
443 : 1258 : unixctl_server_destroy(unixctl);
444 : 1258 : replication_destroy();
445 : :
446 [ + + ][ + - ]: 1258 : if (run_process && process_exited(run_process)) {
447 : 108 : int status = process_status(run_process);
448 [ - + ]: 108 : if (status) {
449 : 0 : ovs_fatal(0, "%s: child exited, %s",
450 : : run_command, process_status_msg(status));
451 : : }
452 : : }
453 : 1258 : perf_counters_destroy();
454 : 1258 : service_stop();
455 : 1258 : return 0;
456 : : }
457 : :
458 : : /* Returns true if 'filename' is known to be already open as a database,
459 : : * false if not.
460 : : *
461 : : * "False negatives" are possible. */
462 : : static bool
463 : 1273 : is_already_open(struct server_config *config OVS_UNUSED,
464 : : const char *filename OVS_UNUSED)
465 : : {
466 : : #ifndef _WIN32
467 : : struct stat s;
468 : :
469 [ + + ]: 1273 : if (!stat(filename, &s)) {
470 : : struct shash_node *node;
471 : :
472 [ + + ][ - + ]: 1283 : SHASH_FOR_EACH (node, config->all_dbs) {
473 : 12 : struct db *db = node->data;
474 : : struct stat s2;
475 : :
476 [ + - ]: 12 : if (!stat(db->filename, &s2)
477 [ + - ]: 12 : && s.st_dev == s2.st_dev
478 [ + + ]: 12 : && s.st_ino == s2.st_ino) {
479 : 1 : return true;
480 : : }
481 : : }
482 : : }
483 : : #endif /* !_WIN32 */
484 : :
485 : 1273 : return false;
486 : : }
487 : :
488 : : static void
489 : 1270 : close_db(struct db *db)
490 : : {
491 : 1270 : ovsdb_destroy(db->db);
492 : 1270 : free(db->filename);
493 : 1270 : free(db);
494 : 1270 : }
495 : :
496 : : static char *
497 : 1273 : open_db(struct server_config *config, const char *filename)
498 : : {
499 : : struct ovsdb_error *db_error;
500 : : struct db *db;
501 : : char *error;
502 : :
503 : : /* If we know that the file is already open, return a good error message.
504 : : * Otherwise, if the file is open, we'll fail later on with a harder to
505 : : * interpret file locking error. */
506 [ + + ]: 1273 : if (is_already_open(config, filename)) {
507 : 1 : return xasprintf("%s: already open", filename);
508 : : }
509 : :
510 : 1272 : db = xzalloc(sizeof *db);
511 : 1272 : db->filename = xstrdup(filename);
512 : :
513 : 1272 : db_error = ovsdb_file_open(db->filename, false, &db->db, &db->file);
514 [ + + ]: 1272 : if (db_error) {
515 : 1 : error = ovsdb_error_to_string(db_error);
516 [ - + ]: 1271 : } else if (!ovsdb_jsonrpc_server_add_db(config->jsonrpc, db->db)) {
517 : 0 : error = xasprintf("%s: duplicate database name", db->db->schema->name);
518 : : } else {
519 : 1271 : shash_add_assert(config->all_dbs, db->db->schema->name, db);
520 : 1271 : return NULL;
521 : : }
522 : :
523 : 1 : ovsdb_error_destroy(db_error);
524 : 1 : close_db(db);
525 : 1 : return error;
526 : : }
527 : :
528 : : static const struct db *
529 : 100 : find_db(const struct shash *all_dbs, const char *db_name)
530 : : {
531 : : struct shash_node *node;
532 : :
533 [ + + ][ - + ]: 123 : SHASH_FOR_EACH (node, all_dbs) {
534 : 89 : struct db *db = node->data;
535 [ + + ]: 89 : if (!strcmp(db->db->schema->name, db_name)) {
536 : 66 : return db;
537 : : }
538 : : }
539 : :
540 : 34 : return NULL;
541 : : }
542 : :
543 : : static char * OVS_WARN_UNUSED_RESULT
544 : 100 : parse_db_column__(const struct shash *all_dbs,
545 : : const char *name_, char *name,
546 : : const struct db **dbp,
547 : : const struct ovsdb_table **tablep,
548 : : const struct ovsdb_column **columnp)
549 : : {
550 : : const char *db_name, *table_name, *column_name;
551 : : const struct ovsdb_column *column;
552 : : const struct ovsdb_table *table;
553 : : const char *tokens[3];
554 : 100 : char *save_ptr = NULL;
555 : : const struct db *db;
556 : :
557 : 100 : *dbp = NULL;
558 : 100 : *tablep = NULL;
559 : 100 : *columnp = NULL;
560 : :
561 : 100 : strtok_r(name, ":", &save_ptr); /* "db:" */
562 : 100 : tokens[0] = strtok_r(NULL, ",", &save_ptr);
563 : 100 : tokens[1] = strtok_r(NULL, ",", &save_ptr);
564 : 100 : tokens[2] = strtok_r(NULL, ",", &save_ptr);
565 [ + - ][ + - ]: 100 : if (!tokens[0] || !tokens[1] || !tokens[2]) {
[ - + ]
566 : 0 : return xasprintf("\"%s\": invalid syntax", name_);
567 : : }
568 : :
569 : 100 : db_name = tokens[0];
570 : 100 : table_name = tokens[1];
571 : 100 : column_name = tokens[2];
572 : :
573 : 100 : db = find_db(all_dbs, tokens[0]);
574 [ + + ]: 100 : if (!db) {
575 : 34 : return xasprintf("\"%s\": no database named %s", name_, db_name);
576 : : }
577 : :
578 : 66 : table = ovsdb_get_table(db->db, table_name);
579 [ - + ]: 66 : if (!table) {
580 : 0 : return xasprintf("\"%s\": no table named %s", name_, table_name);
581 : : }
582 : :
583 : 66 : column = ovsdb_table_schema_get_column(table->schema, column_name);
584 [ - + ]: 66 : if (!column) {
585 : 0 : return xasprintf("\"%s\": table \"%s\" has no column \"%s\"",
586 : : name_, table_name, column_name);
587 : : }
588 : :
589 : 66 : *dbp = db;
590 : 66 : *columnp = column;
591 : 66 : *tablep = table;
592 : 100 : return NULL;
593 : : }
594 : :
595 : : /* Returns NULL if successful, otherwise a malloc()'d string describing the
596 : : * error. */
597 : : static char * OVS_WARN_UNUSED_RESULT
598 : 100 : parse_db_column(const struct shash *all_dbs,
599 : : const char *name_,
600 : : const struct db **dbp,
601 : : const struct ovsdb_table **tablep,
602 : : const struct ovsdb_column **columnp)
603 : : {
604 : 100 : char *name = xstrdup(name_);
605 : 100 : char *retval = parse_db_column__(all_dbs, name_, name,
606 : : dbp, tablep, columnp);
607 : 100 : free(name);
608 : 100 : return retval;
609 : : }
610 : :
611 : : /* Returns NULL if successful, otherwise a malloc()'d string describing the
612 : : * error. */
613 : : static char * OVS_WARN_UNUSED_RESULT
614 : 33 : parse_db_string_column(const struct shash *all_dbs,
615 : : const char *name,
616 : : const struct db **dbp,
617 : : const struct ovsdb_table **tablep,
618 : : const struct ovsdb_column **columnp)
619 : : {
620 : : char *retval;
621 : :
622 : 33 : retval = parse_db_column(all_dbs, name, dbp, tablep, columnp);
623 [ - + ]: 33 : if (retval) {
624 : 0 : return retval;
625 : : }
626 : :
627 [ + - ]: 33 : if ((*columnp)->type.key.type != OVSDB_TYPE_STRING
628 [ - + ]: 33 : || (*columnp)->type.value.type != OVSDB_TYPE_VOID) {
629 : 0 : return xasprintf("\"%s\": table \"%s\" column \"%s\" is "
630 : : "not string or set of strings",
631 : 0 : name, (*tablep)->schema->name, (*columnp)->name);
632 : : }
633 : :
634 : 33 : return NULL;
635 : : }
636 : :
637 : : static const char *
638 : 180792 : query_db_string(const struct shash *all_dbs, const char *name,
639 : : struct ds *errors)
640 : : {
641 [ + + ][ + + ]: 180792 : if (!name || strncmp(name, "db:", 3)) {
642 : 180759 : return name;
643 : : } else {
644 : : const struct ovsdb_column *column;
645 : : const struct ovsdb_table *table;
646 : : const struct ovsdb_row *row;
647 : : const struct db *db;
648 : : char *retval;
649 : :
650 : 33 : retval = parse_db_string_column(all_dbs, name,
651 : : &db, &table, &column);
652 [ - + ]: 33 : if (retval) {
653 : 0 : ds_put_format(errors, "%s\n", retval);
654 : 0 : free(retval);
655 : 0 : return NULL;
656 : : }
657 : :
658 [ + - ][ # # ]: 33 : HMAP_FOR_EACH (row, hmap_node, &table->rows) {
659 : : const struct ovsdb_datum *datum;
660 : : size_t i;
661 : :
662 : 33 : datum = &row->fields[column->index];
663 [ + - ]: 33 : for (i = 0; i < datum->n; i++) {
664 [ + - ]: 33 : if (datum->keys[i].string[0]) {
665 : 33 : return datum->keys[i].string;
666 : : }
667 : : }
668 : : }
669 : 33 : return NULL;
670 : : }
671 : : }
672 : :
673 : : static struct ovsdb_jsonrpc_options *
674 : 60869 : add_remote(struct shash *remotes, const char *target)
675 : : {
676 : : struct ovsdb_jsonrpc_options *options;
677 : :
678 : 60869 : options = shash_find_data(remotes, target);
679 [ + - ]: 60869 : if (!options) {
680 : 60869 : options = ovsdb_jsonrpc_default_options(target);
681 : 60869 : shash_add(remotes, target, options);
682 : : }
683 : :
684 : 60869 : return options;
685 : : }
686 : :
687 : : static struct ovsdb_datum *
688 : 51 : get_datum(struct ovsdb_row *row, const char *column_name,
689 : : const enum ovsdb_atomic_type key_type,
690 : : const enum ovsdb_atomic_type value_type,
691 : : const size_t n_max)
692 : : {
693 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
694 : 51 : const struct ovsdb_table_schema *schema = row->table->schema;
695 : : const struct ovsdb_column *column;
696 : :
697 : 51 : column = ovsdb_table_schema_get_column(schema, column_name);
698 [ + + ]: 51 : if (!column) {
699 [ - + ]: 37 : VLOG_DBG_RL(&rl, "Table `%s' has no `%s' column",
700 : : schema->name, column_name);
701 : 37 : return NULL;
702 : : }
703 : :
704 [ + - ]: 14 : if (column->type.key.type != key_type
705 [ + - ]: 14 : || column->type.value.type != value_type
706 [ - + ]: 14 : || column->type.n_max != n_max) {
707 [ # # ]: 0 : if (!VLOG_DROP_DBG(&rl)) {
708 : 0 : char *type_name = ovsdb_type_to_english(&column->type);
709 [ # # ]: 0 : VLOG_DBG("Table `%s' column `%s' has type %s, not expected "
710 : : "key type %s, value type %s, max elements %"PRIuSIZE".",
711 : : schema->name, column_name, type_name,
712 : : ovsdb_atomic_type_to_string(key_type),
713 : : ovsdb_atomic_type_to_string(value_type),
714 : : n_max);
715 : 0 : free(type_name);
716 : : }
717 : 0 : return NULL;
718 : : }
719 : :
720 : 14 : return &row->fields[column->index];
721 : : }
722 : :
723 : : /* Read string-string key-values from a map. Returns the value associated with
724 : : * 'key', if found, or NULL */
725 : : static const char *
726 : 12 : read_map_string_column(const struct ovsdb_row *row, const char *column_name,
727 : : const char *key)
728 : : {
729 : : const struct ovsdb_datum *datum;
730 : 12 : union ovsdb_atom *atom_key = NULL, *atom_value = NULL;
731 : : size_t i;
732 : :
733 : 12 : datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name,
734 : : OVSDB_TYPE_STRING, OVSDB_TYPE_STRING, UINT_MAX);
735 : :
736 [ + - ]: 12 : if (!datum) {
737 : 12 : return NULL;
738 : : }
739 : :
740 [ # # ]: 0 : for (i = 0; i < datum->n; i++) {
741 : 0 : atom_key = &datum->keys[i];
742 [ # # ]: 0 : if (!strcmp(atom_key->string, key)){
743 : 0 : atom_value = &datum->values[i];
744 : 0 : break;
745 : : }
746 : : }
747 : :
748 [ # # ]: 0 : return atom_value ? atom_value->string : NULL;
749 : : }
750 : :
751 : : static const union ovsdb_atom *
752 : 37 : read_column(const struct ovsdb_row *row, const char *column_name,
753 : : enum ovsdb_atomic_type type)
754 : : {
755 : : const struct ovsdb_datum *datum;
756 : :
757 : 37 : datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name, type,
758 : : OVSDB_TYPE_VOID, 1);
759 [ + + ][ + - ]: 37 : return datum && datum->n ? datum->keys : NULL;
760 : : }
761 : :
762 : : static bool
763 : 24 : read_integer_column(const struct ovsdb_row *row, const char *column_name,
764 : : long long int *integerp)
765 : : {
766 : : const union ovsdb_atom *atom;
767 : :
768 : 24 : atom = read_column(row, column_name, OVSDB_TYPE_INTEGER);
769 [ - + ]: 24 : *integerp = atom ? atom->integer : 0;
770 : 24 : return atom != NULL;
771 : : }
772 : :
773 : : static bool
774 : 13 : read_string_column(const struct ovsdb_row *row, const char *column_name,
775 : : const char **stringp)
776 : : {
777 : : const union ovsdb_atom *atom;
778 : :
779 : 13 : atom = read_column(row, column_name, OVSDB_TYPE_STRING);
780 [ + - ]: 13 : *stringp = atom ? atom->string : NULL;
781 : 13 : return atom != NULL;
782 : : }
783 : :
784 : : static void
785 : 1 : write_bool_column(struct ovsdb_row *row, const char *column_name, bool value)
786 : : {
787 : : const struct ovsdb_column *column;
788 : : struct ovsdb_datum *datum;
789 : :
790 : 1 : column = ovsdb_table_schema_get_column(row->table->schema, column_name);
791 : 1 : datum = get_datum(row, column_name, OVSDB_TYPE_BOOLEAN,
792 : : OVSDB_TYPE_VOID, 1);
793 [ - + ]: 1 : if (!datum) {
794 : 0 : return;
795 : : }
796 : :
797 [ + - ]: 1 : if (datum->n != 1) {
798 : 1 : ovsdb_datum_destroy(datum, &column->type);
799 : :
800 : 1 : datum->n = 1;
801 : 1 : datum->keys = xmalloc(sizeof *datum->keys);
802 : 1 : datum->values = NULL;
803 : : }
804 : :
805 : 1 : datum->keys[0].boolean = value;
806 : : }
807 : :
808 : : static void
809 : 1 : write_string_string_column(struct ovsdb_row *row, const char *column_name,
810 : : char **keys, char **values, size_t n)
811 : : {
812 : : const struct ovsdb_column *column;
813 : : struct ovsdb_datum *datum;
814 : : size_t i;
815 : :
816 : 1 : column = ovsdb_table_schema_get_column(row->table->schema, column_name);
817 : 1 : datum = get_datum(row, column_name, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING,
818 : : UINT_MAX);
819 [ + - ]: 1 : if (!datum) {
820 [ + + ]: 3 : for (i = 0; i < n; i++) {
821 : 2 : free(keys[i]);
822 : 2 : free(values[i]);
823 : : }
824 : 1 : return;
825 : : }
826 : :
827 : : /* Free existing data. */
828 : 0 : ovsdb_datum_destroy(datum, &column->type);
829 : :
830 : : /* Allocate space for new values. */
831 : 0 : datum->n = n;
832 : 0 : datum->keys = xmalloc(n * sizeof *datum->keys);
833 : 0 : datum->values = xmalloc(n * sizeof *datum->values);
834 : :
835 [ # # ]: 0 : for (i = 0; i < n; ++i) {
836 : 0 : datum->keys[i].string = keys[i];
837 : 0 : datum->values[i].string = values[i];
838 : : }
839 : :
840 : : /* Sort and check constraints. */
841 : 0 : ovsdb_datum_sort_assert(datum, column->type.key.type);
842 : : }
843 : :
844 : : /* Adds a remote and options to 'remotes', based on the Manager table row in
845 : : * 'row'. */
846 : : static void
847 : 12 : add_manager_options(struct shash *remotes, const struct ovsdb_row *row)
848 : : {
849 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
850 : : struct ovsdb_jsonrpc_options *options;
851 : : long long int max_backoff, probe_interval;
852 : : const char *target, *dscp_string;
853 : :
854 [ + - ][ - + ]: 12 : if (!read_string_column(row, "target", &target) || !target) {
855 [ # # ]: 0 : VLOG_INFO_RL(&rl, "Table `%s' has missing or invalid `target' column",
856 : : row->table->schema->name);
857 : 0 : return;
858 : : }
859 : :
860 : 12 : options = add_remote(remotes, target);
861 [ - + ]: 12 : if (read_integer_column(row, "max_backoff", &max_backoff)) {
862 : 0 : options->max_backoff = max_backoff;
863 : : }
864 [ - + ]: 12 : if (read_integer_column(row, "inactivity_probe", &probe_interval)) {
865 : 0 : options->probe_interval = probe_interval;
866 : : }
867 : :
868 : 12 : options->dscp = DSCP_DEFAULT;
869 : 12 : dscp_string = read_map_string_column(row, "other_config", "dscp");
870 [ - + ]: 12 : if (dscp_string) {
871 : 0 : int dscp = atoi(dscp_string);
872 [ # # ][ # # ]: 0 : if (dscp >= 0 && dscp <= 63) {
873 : 12 : options->dscp = dscp;
874 : : }
875 : : }
876 : : }
877 : :
878 : : static void
879 : 63 : query_db_remotes(const char *name, const struct shash *all_dbs,
880 : : struct shash *remotes, struct ds *errors)
881 : : {
882 : : const struct ovsdb_column *column;
883 : : const struct ovsdb_table *table;
884 : : const struct ovsdb_row *row;
885 : : const struct db *db;
886 : : char *retval;
887 : :
888 : 63 : retval = parse_db_column(all_dbs, name, &db, &table, &column);
889 [ + + ]: 63 : if (retval) {
890 : 33 : ds_put_format(errors, "%s\n", retval);
891 : 33 : free(retval);
892 : 33 : return;
893 : : }
894 : :
895 [ + + ]: 48 : if (column->type.key.type == OVSDB_TYPE_STRING
896 [ + - ]: 18 : && column->type.value.type == OVSDB_TYPE_VOID) {
897 [ + + ][ - + ]: 30 : HMAP_FOR_EACH (row, hmap_node, &table->rows) {
898 : : const struct ovsdb_datum *datum;
899 : : size_t i;
900 : :
901 : 12 : datum = &row->fields[column->index];
902 [ + + ]: 24 : for (i = 0; i < datum->n; i++) {
903 : 12 : add_remote(remotes, datum->keys[i].string);
904 : : }
905 : : }
906 [ + - ]: 12 : } else if (column->type.key.type == OVSDB_TYPE_UUID
907 [ + - ]: 12 : && column->type.key.u.uuid.refTable
908 [ + - ]: 12 : && column->type.value.type == OVSDB_TYPE_VOID) {
909 : 12 : const struct ovsdb_table *ref_table = column->type.key.u.uuid.refTable;
910 [ + + ][ - + ]: 42 : HMAP_FOR_EACH (row, hmap_node, &table->rows) {
911 : : const struct ovsdb_datum *datum;
912 : : size_t i;
913 : :
914 : 12 : datum = &row->fields[column->index];
915 [ + + ]: 24 : for (i = 0; i < datum->n; i++) {
916 : : const struct ovsdb_row *ref_row;
917 : :
918 : 12 : ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid);
919 [ + - ]: 12 : if (ref_row) {
920 : 12 : add_manager_options(remotes, ref_row);
921 : : }
922 : : }
923 : : }
924 : : }
925 : : }
926 : :
927 : : static void
928 : 1 : update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn,
929 : : const struct ovsdb_jsonrpc_server *jsonrpc)
930 : : {
931 : : struct ovsdb_jsonrpc_remote_status status;
932 : : struct ovsdb_row *rw_row;
933 : : const char *target;
934 : : char *keys[9], *values[9];
935 : 1 : size_t n = 0;
936 : :
937 : : /* Get the "target" (protocol/host/port) spec. */
938 [ - + ]: 1 : if (!read_string_column(row, "target", &target)) {
939 : : /* Bad remote spec or incorrect schema. */
940 : 0 : return;
941 : : }
942 : 1 : rw_row = ovsdb_txn_row_modify(txn, row);
943 : 1 : ovsdb_jsonrpc_server_get_remote_status(jsonrpc, target, &status);
944 : :
945 : : /* Update status information columns. */
946 : 1 : write_bool_column(rw_row, "is_connected", status.is_connected);
947 : :
948 [ - + ]: 1 : if (status.state) {
949 : 0 : keys[n] = xstrdup("state");
950 : 0 : values[n++] = xstrdup(status.state);
951 : : }
952 [ + - ]: 1 : if (status.sec_since_connect != UINT_MAX) {
953 : 1 : keys[n] = xstrdup("sec_since_connect");
954 : 1 : values[n++] = xasprintf("%u", status.sec_since_connect);
955 : : }
956 [ + - ]: 1 : if (status.sec_since_disconnect != UINT_MAX) {
957 : 1 : keys[n] = xstrdup("sec_since_disconnect");
958 : 1 : values[n++] = xasprintf("%u", status.sec_since_disconnect);
959 : : }
960 [ - + ]: 1 : if (status.last_error) {
961 : 0 : keys[n] = xstrdup("last_error");
962 : 0 : values[n++] =
963 : 0 : xstrdup(ovs_retval_to_string(status.last_error));
964 : : }
965 [ - + ][ # # ]: 1 : if (status.locks_held && status.locks_held[0]) {
966 : 0 : keys[n] = xstrdup("locks_held");
967 : 0 : values[n++] = xstrdup(status.locks_held);
968 : : }
969 [ - + ][ # # ]: 1 : if (status.locks_waiting && status.locks_waiting[0]) {
970 : 0 : keys[n] = xstrdup("locks_waiting");
971 : 0 : values[n++] = xstrdup(status.locks_waiting);
972 : : }
973 [ - + ][ # # ]: 1 : if (status.locks_lost && status.locks_lost[0]) {
974 : 0 : keys[n] = xstrdup("locks_lost");
975 : 0 : values[n++] = xstrdup(status.locks_lost);
976 : : }
977 [ - + ]: 1 : if (status.n_connections > 1) {
978 : 0 : keys[n] = xstrdup("n_connections");
979 : 0 : values[n++] = xasprintf("%d", status.n_connections);
980 : : }
981 [ - + ]: 1 : if (status.bound_port != htons(0)) {
982 : 0 : keys[n] = xstrdup("bound_port");
983 : 0 : values[n++] = xasprintf("%"PRIu16, ntohs(status.bound_port));
984 : : }
985 : 1 : write_string_string_column(rw_row, "status", keys, values, n);
986 : :
987 : 1 : ovsdb_jsonrpc_server_free_remote_status(&status);
988 : : }
989 : :
990 : : static void
991 : 2713 : update_remote_rows(const struct shash *all_dbs,
992 : : const char *remote_name,
993 : : const struct ovsdb_jsonrpc_server *jsonrpc)
994 : : {
995 : : const struct ovsdb_table *table, *ref_table;
996 : : const struct ovsdb_column *column;
997 : : const struct ovsdb_row *row;
998 : : const struct db *db;
999 : : char *retval;
1000 : :
1001 [ + + ]: 2713 : if (strncmp("db:", remote_name, 3)) {
1002 : 2712 : return;
1003 : : }
1004 : :
1005 : 2 : retval = parse_db_column(all_dbs, remote_name, &db, &table, &column);
1006 [ - + ]: 2 : if (retval) {
1007 : 0 : free(retval);
1008 : 0 : return;
1009 : : }
1010 : :
1011 [ + + ]: 2 : if (column->type.key.type != OVSDB_TYPE_UUID
1012 [ + - ]: 1 : || !column->type.key.u.uuid.refTable
1013 [ - + ]: 1 : || column->type.value.type != OVSDB_TYPE_VOID) {
1014 : 1 : return;
1015 : : }
1016 : :
1017 : 1 : ref_table = column->type.key.u.uuid.refTable;
1018 : :
1019 [ + + ][ - + ]: 2 : HMAP_FOR_EACH (row, hmap_node, &table->rows) {
1020 : : const struct ovsdb_datum *datum;
1021 : : size_t i;
1022 : :
1023 : 1 : datum = &row->fields[column->index];
1024 [ + + ]: 2 : for (i = 0; i < datum->n; i++) {
1025 : : const struct ovsdb_row *ref_row;
1026 : :
1027 : 1 : ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid);
1028 [ + - ]: 1 : if (ref_row) {
1029 : 1 : update_remote_row(ref_row, db->txn, jsonrpc);
1030 : : }
1031 : : }
1032 : : }
1033 : : }
1034 : :
1035 : : static void
1036 : 2679 : update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc,
1037 : : const struct sset *remotes,
1038 : : struct shash *all_dbs)
1039 : : {
1040 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
1041 : : const char *remote;
1042 : : struct db *db;
1043 : : struct shash_node *node;
1044 : :
1045 [ + + ][ - + ]: 5392 : SHASH_FOR_EACH(node, all_dbs) {
1046 : 2713 : db = node->data;
1047 : 2713 : db->txn = ovsdb_txn_create(db->db);
1048 : : }
1049 : :
1050 : : /* Iterate over --remote arguments given on command line. */
1051 [ + + ][ + + ]: 5392 : SSET_FOR_EACH (remote, remotes) {
[ + + ]
1052 : 2713 : update_remote_rows(all_dbs, remote, jsonrpc);
1053 : : }
1054 : :
1055 [ + + ][ - + ]: 5392 : SHASH_FOR_EACH(node, all_dbs) {
1056 : : struct ovsdb_error *error;
1057 : 2713 : db = node->data;
1058 : 2713 : error = ovsdb_txn_commit(db->txn, false);
1059 [ - + ]: 2713 : if (error) {
1060 [ # # ]: 0 : VLOG_ERR_RL(&rl, "Failed to update remote status: %s",
1061 : : ovsdb_error_to_string(error));
1062 : 0 : ovsdb_error_destroy(error);
1063 : : }
1064 : : }
1065 : 2679 : }
1066 : :
1067 : : /* Reconfigures ovsdb-server's remotes based on information in the database. */
1068 : : static char *
1069 : 60264 : reconfigure_remotes(struct ovsdb_jsonrpc_server *jsonrpc,
1070 : : const struct shash *all_dbs, struct sset *remotes)
1071 : : {
1072 : 60264 : struct ds errors = DS_EMPTY_INITIALIZER;
1073 : : struct shash resolved_remotes;
1074 : : const char *name;
1075 : :
1076 : : /* Configure remotes. */
1077 : 60264 : shash_init(&resolved_remotes);
1078 [ + + ][ + + ]: 121172 : SSET_FOR_EACH (name, remotes) {
[ + + ]
1079 [ + + ]: 60908 : if (!strncmp(name, "db:", 3)) {
1080 : 63 : query_db_remotes(name, all_dbs, &resolved_remotes, &errors);
1081 : : } else {
1082 : 60845 : add_remote(&resolved_remotes, name);
1083 : : }
1084 : : }
1085 : 60264 : ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes);
1086 : 60264 : shash_destroy_free_data(&resolved_remotes);
1087 : :
1088 : 60264 : return errors.string;
1089 : : }
1090 : :
1091 : : static char *
1092 : 60264 : reconfigure_ssl(const struct shash *all_dbs)
1093 : : {
1094 : 60264 : struct ds errors = DS_EMPTY_INITIALIZER;
1095 : : const char *resolved_private_key;
1096 : : const char *resolved_certificate;
1097 : : const char *resolved_ca_cert;
1098 : :
1099 : 60264 : resolved_private_key = query_db_string(all_dbs, private_key_file, &errors);
1100 : 60264 : resolved_certificate = query_db_string(all_dbs, certificate_file, &errors);
1101 : 60264 : resolved_ca_cert = query_db_string(all_dbs, ca_cert_file, &errors);
1102 : :
1103 : 60264 : stream_ssl_set_key_and_cert(resolved_private_key, resolved_certificate);
1104 : 60264 : stream_ssl_set_ca_cert_file(resolved_ca_cert, bootstrap_ca_cert);
1105 : :
1106 : 60264 : return errors.string;
1107 : : }
1108 : :
1109 : : static void
1110 : 118008 : report_error_if_changed(char *error, char **last_errorp)
1111 : : {
1112 [ + + ]: 118008 : if (error) {
1113 [ + + ][ - + ]: 33 : if (!*last_errorp || strcmp(error, *last_errorp)) {
1114 [ + - ]: 1 : VLOG_WARN("%s", error);
1115 : 1 : free(*last_errorp);
1116 : 1 : *last_errorp = error;
1117 : 1 : return;
1118 : : }
1119 : 32 : free(error);
1120 : : } else {
1121 : 117975 : free(*last_errorp);
1122 : 117975 : *last_errorp = NULL;
1123 : : }
1124 : : }
1125 : :
1126 : : static void
1127 : 1 : ovsdb_server_set_active_ovsdb_server(struct unixctl_conn *conn,
1128 : : int argc OVS_UNUSED, const char *argv[],
1129 : : void *config_)
1130 : : {
1131 : 1 : struct server_config *config = config_;
1132 : :
1133 [ - + ]: 1 : if (*config->sync_from) {
1134 : 0 : free(*config->sync_from);
1135 : : }
1136 : 1 : *config->sync_from = xstrdup(argv[1]);
1137 : 1 : save_config(config);
1138 : :
1139 : 1 : unixctl_command_reply(conn, NULL);
1140 : 1 : }
1141 : :
1142 : : static void
1143 : 0 : ovsdb_server_get_active_ovsdb_server(struct unixctl_conn *conn,
1144 : : int argc OVS_UNUSED,
1145 : : const char *argv[] OVS_UNUSED,
1146 : : void *config_ )
1147 : : {
1148 : 0 : struct server_config *config = config_;
1149 : :
1150 : 0 : unixctl_command_reply(conn, *config->sync_from);
1151 : 0 : }
1152 : :
1153 : : static void
1154 : 2 : ovsdb_server_connect_active_ovsdb_server(struct unixctl_conn *conn,
1155 : : int argc OVS_UNUSED,
1156 : : const char *argv[] OVS_UNUSED,
1157 : : void *config_)
1158 : : {
1159 : 2 : struct server_config *config = config_;
1160 : 2 : char *msg = NULL;
1161 : :
1162 [ + + ]: 2 : if ( !*config->sync_from) {
1163 : 1 : msg = "Unable to connect: active server is not specified.\n";
1164 : : } else {
1165 : 1 : ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
1166 : : config->all_dbs);
1167 [ + - ]: 1 : if (!*config->is_backup) {
1168 : 1 : *config->is_backup = true;
1169 : 1 : save_config(config);
1170 : : }
1171 : : }
1172 : 2 : unixctl_command_reply(conn, msg);
1173 : 2 : }
1174 : :
1175 : : static void
1176 : 1 : ovsdb_server_disconnect_active_ovsdb_server(struct unixctl_conn *conn,
1177 : : int argc OVS_UNUSED,
1178 : : const char *argv[] OVS_UNUSED,
1179 : : void *config_)
1180 : : {
1181 : 1 : struct server_config *config = config_;
1182 : :
1183 : 1 : disconnect_active_server();
1184 : 1 : *config->is_backup = false;
1185 : 1 : save_config(config);
1186 : 1 : unixctl_command_reply(conn, NULL);
1187 : 1 : }
1188 : :
1189 : : static void
1190 : 1 : ovsdb_server_set_sync_exclude_tables(struct unixctl_conn *conn,
1191 : : int argc OVS_UNUSED,
1192 : : const char *argv[],
1193 : : void *config_)
1194 : : {
1195 : 1 : struct server_config *config = config_;
1196 : :
1197 : 1 : char *err = set_blacklist_tables(argv[1], true);
1198 [ + - ]: 1 : if (!err) {
1199 : 1 : free(*config->sync_exclude);
1200 : 1 : *config->sync_exclude = xstrdup(argv[1]);
1201 : 1 : save_config(config);
1202 [ + - ]: 1 : if (*config->is_backup) {
1203 : 1 : ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
1204 : : config->all_dbs);
1205 : : }
1206 : 1 : err = set_blacklist_tables(argv[1], false);
1207 : : }
1208 : 1 : unixctl_command_reply(conn, err);
1209 : 1 : free(err);
1210 : 1 : }
1211 : :
1212 : : static void
1213 : 0 : ovsdb_server_get_sync_exclude_tables(struct unixctl_conn *conn,
1214 : : int argc OVS_UNUSED,
1215 : : const char *argv[] OVS_UNUSED,
1216 : : void *arg_ OVS_UNUSED)
1217 : : {
1218 : 0 : char *reply = get_blacklist_tables();
1219 : 0 : unixctl_command_reply(conn, reply);
1220 : 0 : free(reply);
1221 : 0 : }
1222 : :
1223 : : static void
1224 : 1150 : ovsdb_server_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
1225 : : const char *argv[] OVS_UNUSED,
1226 : : void *exiting_)
1227 : : {
1228 : 1150 : bool *exiting = exiting_;
1229 : 1150 : *exiting = true;
1230 : 1150 : unixctl_command_reply(conn, NULL);
1231 : 1150 : }
1232 : :
1233 : : static void
1234 : 0 : ovsdb_server_perf_counters_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
1235 : : const char *argv[] OVS_UNUSED,
1236 : : void *arg_ OVS_UNUSED)
1237 : : {
1238 : 0 : char *s = perf_counters_to_string();
1239 : :
1240 : 0 : unixctl_command_reply(conn, s);
1241 : 0 : free(s);
1242 : 0 : }
1243 : :
1244 : : static void
1245 : 0 : ovsdb_server_perf_counters_clear(struct unixctl_conn *conn, int argc OVS_UNUSED,
1246 : : const char *argv[] OVS_UNUSED,
1247 : : void *arg_ OVS_UNUSED)
1248 : : {
1249 : 0 : perf_counters_clear();
1250 : 0 : unixctl_command_reply(conn, NULL);
1251 : 0 : }
1252 : :
1253 : : /* "ovsdb-server/disable-monitor-cond": makes ovsdb-server drop all of its
1254 : : * JSON-RPC connections and reconnect. New sessions will not recognize
1255 : : * the 'monitor_cond' method. */
1256 : : static void
1257 : 2 : ovsdb_server_disable_monitor_cond(struct unixctl_conn *conn,
1258 : : int argc OVS_UNUSED,
1259 : : const char *argv[] OVS_UNUSED,
1260 : : void *jsonrpc_)
1261 : : {
1262 : 2 : struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_;
1263 : 2 : bool read_only = ovsdb_jsonrpc_server_is_read_only(jsonrpc);
1264 : :
1265 : 2 : ovsdb_jsonrpc_disable_monitor_cond();
1266 : 2 : ovsdb_jsonrpc_server_reconnect(jsonrpc, read_only);
1267 : 2 : unixctl_command_reply(conn, NULL);
1268 : 2 : }
1269 : :
1270 : : static void
1271 : 1 : ovsdb_server_compact(struct unixctl_conn *conn, int argc,
1272 : : const char *argv[], void *dbs_)
1273 : : {
1274 : 1 : struct shash *all_dbs = dbs_;
1275 : : struct ds reply;
1276 : : struct db *db;
1277 : : struct shash_node *node;
1278 : 1 : int n = 0;
1279 : :
1280 : 1 : ds_init(&reply);
1281 [ + + ][ - + ]: 2 : SHASH_FOR_EACH(node, all_dbs) {
1282 : : const char *name;
1283 : :
1284 : 1 : db = node->data;
1285 : 1 : name = db->db->schema->name;
1286 : :
1287 [ - + ][ # # ]: 1 : if (argc < 2 || !strcmp(argv[1], name)) {
1288 : : struct ovsdb_error *error;
1289 : :
1290 [ + - ]: 1 : VLOG_INFO("compacting %s database by user request", name);
1291 : :
1292 : 1 : error = ovsdb_file_compact(db->file);
1293 [ - + ]: 1 : if (error) {
1294 : 0 : char *s = ovsdb_error_to_string(error);
1295 : 0 : ds_put_format(&reply, "%s\n", s);
1296 : 0 : free(s);
1297 : 0 : ovsdb_error_destroy(error);
1298 : : }
1299 : :
1300 : 1 : n++;
1301 : : }
1302 : : }
1303 : :
1304 [ - + ]: 1 : if (!n) {
1305 : 0 : unixctl_command_reply_error(conn, "no database by that name");
1306 [ - + ]: 1 : } else if (reply.length) {
1307 : 0 : unixctl_command_reply_error(conn, ds_cstr(&reply));
1308 : : } else {
1309 : 1 : unixctl_command_reply(conn, NULL);
1310 : : }
1311 : 1 : ds_destroy(&reply);
1312 : 1 : }
1313 : :
1314 : : /* "ovsdb-server/reconnect": makes ovsdb-server drop all of its JSON-RPC
1315 : : * connections and reconnect. */
1316 : : static void
1317 : 0 : ovsdb_server_reconnect(struct unixctl_conn *conn, int argc OVS_UNUSED,
1318 : : const char *argv[] OVS_UNUSED, void *jsonrpc_)
1319 : : {
1320 : 0 : struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_;
1321 : 0 : bool read_only = ovsdb_jsonrpc_server_is_read_only(jsonrpc);
1322 : :
1323 : 0 : ovsdb_jsonrpc_server_reconnect(jsonrpc, read_only);
1324 : 0 : unixctl_command_reply(conn, NULL);
1325 : 0 : }
1326 : :
1327 : : /* "ovsdb-server/add-remote REMOTE": adds REMOTE to the set of remotes that
1328 : : * ovsdb-server services. */
1329 : : static void
1330 : 4 : ovsdb_server_add_remote(struct unixctl_conn *conn, int argc OVS_UNUSED,
1331 : : const char *argv[], void *config_)
1332 : : {
1333 : 4 : struct server_config *config = config_;
1334 : 4 : const char *remote = argv[1];
1335 : :
1336 : : const struct ovsdb_column *column;
1337 : : const struct ovsdb_table *table;
1338 : : const struct db *db;
1339 : : char *retval;
1340 : :
1341 : 8 : retval = (strncmp("db:", remote, 3)
1342 : : ? NULL
1343 [ + + ]: 4 : : parse_db_column(config->all_dbs, remote,
1344 : : &db, &table, &column));
1345 [ + + ]: 4 : if (!retval) {
1346 [ + - ]: 3 : if (sset_add(config->remotes, remote)) {
1347 : 3 : save_config(config);
1348 : : }
1349 : 3 : unixctl_command_reply(conn, NULL);
1350 : : } else {
1351 : 1 : unixctl_command_reply_error(conn, retval);
1352 : 1 : free(retval);
1353 : : }
1354 : 4 : }
1355 : :
1356 : : /* "ovsdb-server/remove-remote REMOTE": removes REMOTE frmo the set of remotes
1357 : : * that ovsdb-server services. */
1358 : : static void
1359 : 2 : ovsdb_server_remove_remote(struct unixctl_conn *conn, int argc OVS_UNUSED,
1360 : : const char *argv[], void *config_)
1361 : : {
1362 : 2 : struct server_config *config = config_;
1363 : : struct sset_node *node;
1364 : :
1365 : 2 : node = sset_find(config->remotes, argv[1]);
1366 [ + - ]: 2 : if (node) {
1367 : 2 : sset_delete(config->remotes, node);
1368 : 2 : save_config(config);
1369 : 2 : unixctl_command_reply(conn, NULL);
1370 : : } else {
1371 : 0 : unixctl_command_reply_error(conn, "no such remote");
1372 : : }
1373 : 2 : }
1374 : :
1375 : : /* "ovsdb-server/list-remotes": outputs a list of configured rmeotes. */
1376 : : static void
1377 : 6 : ovsdb_server_list_remotes(struct unixctl_conn *conn, int argc OVS_UNUSED,
1378 : : const char *argv[] OVS_UNUSED, void *remotes_)
1379 : : {
1380 : 6 : struct sset *remotes = remotes_;
1381 : : const char **list, **p;
1382 : : struct ds s;
1383 : :
1384 : 6 : ds_init(&s);
1385 : :
1386 : 6 : list = sset_sort(remotes);
1387 [ + + ]: 14 : for (p = list; *p; p++) {
1388 : 8 : ds_put_format(&s, "%s\n", *p);
1389 : : }
1390 : 6 : free(list);
1391 : :
1392 : 6 : unixctl_command_reply(conn, ds_cstr(&s));
1393 : 6 : ds_destroy(&s);
1394 : 6 : }
1395 : :
1396 : :
1397 : : /* "ovsdb-server/add-db DB": adds the DB to ovsdb-server. */
1398 : : static void
1399 : 5 : ovsdb_server_add_database(struct unixctl_conn *conn, int argc OVS_UNUSED,
1400 : : const char *argv[], void *config_)
1401 : : {
1402 : 5 : struct server_config *config = config_;
1403 : 5 : const char *filename = argv[1];
1404 : : char *error;
1405 : :
1406 : 5 : error = open_db(config, filename);
1407 [ + + ]: 5 : if (!error) {
1408 : 3 : save_config(config);
1409 [ - + ]: 3 : if (*config->is_backup) {
1410 : 0 : ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
1411 : : config->all_dbs);
1412 : : }
1413 : 3 : unixctl_command_reply(conn, NULL);
1414 : : } else {
1415 : 2 : unixctl_command_reply_error(conn, error);
1416 : 2 : free(error);
1417 : : }
1418 : 5 : }
1419 : :
1420 : : static void
1421 : 3 : ovsdb_server_remove_database(struct unixctl_conn *conn, int argc OVS_UNUSED,
1422 : : const char *argv[], void *config_)
1423 : : {
1424 : 3 : struct server_config *config = config_;
1425 : : struct shash_node *node;
1426 : : struct db *db;
1427 : : bool ok;
1428 : :
1429 : 3 : node = shash_find(config->all_dbs, argv[1]);
1430 [ + + ]: 3 : if (!node) {
1431 : 1 : unixctl_command_reply_error(conn, "Failed to find the database.");
1432 : 1 : return;
1433 : : }
1434 : 2 : db = node->data;
1435 : :
1436 : 2 : ok = ovsdb_jsonrpc_server_remove_db(config->jsonrpc, db->db);
1437 [ - + ]: 2 : ovs_assert(ok);
1438 : :
1439 : 2 : close_db(db);
1440 : 2 : shash_delete(config->all_dbs, node);
1441 : :
1442 : 2 : save_config(config);
1443 [ - + ]: 2 : if (*config->is_backup) {
1444 : 0 : ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
1445 : : config->all_dbs);
1446 : : }
1447 : 2 : unixctl_command_reply(conn, NULL);
1448 : : }
1449 : :
1450 : : static void
1451 : 7 : ovsdb_server_list_databases(struct unixctl_conn *conn, int argc OVS_UNUSED,
1452 : : const char *argv[] OVS_UNUSED, void *all_dbs_)
1453 : : {
1454 : 7 : struct shash *all_dbs = all_dbs_;
1455 : : const struct shash_node **nodes;
1456 : : struct ds s;
1457 : : size_t i;
1458 : :
1459 : 7 : ds_init(&s);
1460 : :
1461 : 7 : nodes = shash_sort(all_dbs);
1462 [ + + ]: 15 : for (i = 0; i < shash_count(all_dbs); i++) {
1463 : 8 : struct db *db = nodes[i]->data;
1464 : 8 : ds_put_format(&s, "%s\n", db->db->schema->name);
1465 : : }
1466 : 7 : free(nodes);
1467 : :
1468 : 7 : unixctl_command_reply(conn, ds_cstr(&s));
1469 : 7 : ds_destroy(&s);
1470 : 7 : }
1471 : :
1472 : : static void
1473 : 0 : ovsdb_server_get_sync_status(struct unixctl_conn *conn, int argc OVS_UNUSED,
1474 : : const char *argv[] OVS_UNUSED, void *config_)
1475 : : {
1476 : 0 : struct server_config *config = config_;
1477 : 0 : bool is_backup = *config->is_backup;
1478 : 0 : struct ds ds = DS_EMPTY_INITIALIZER;
1479 : :
1480 [ # # ]: 0 : ds_put_format(&ds, "state: %s\n", is_backup ? "backup" : "active");
1481 : :
1482 [ # # ]: 0 : if (is_backup) {
1483 : 0 : ds_put_and_free_cstr(&ds, replication_status());
1484 : : }
1485 : :
1486 : 0 : unixctl_command_reply(conn, ds_cstr(&ds));
1487 : 0 : ds_destroy(&ds);
1488 : 0 : }
1489 : :
1490 : : static void
1491 : 1297 : parse_options(int *argcp, char **argvp[],
1492 : : struct sset *remotes, char **unixctl_pathp, char **run_command,
1493 : : char **sync_from, char **sync_exclude, bool *active)
1494 : : {
1495 : : enum {
1496 : : OPT_REMOTE = UCHAR_MAX + 1,
1497 : : OPT_UNIXCTL,
1498 : : OPT_RUN,
1499 : : OPT_BOOTSTRAP_CA_CERT,
1500 : : OPT_PEER_CA_CERT,
1501 : : OPT_SYNC_FROM,
1502 : : OPT_SYNC_EXCLUDE,
1503 : : OPT_ACTIVE,
1504 : : VLOG_OPTION_ENUMS,
1505 : : DAEMON_OPTION_ENUMS
1506 : : };
1507 : : static const struct option long_options[] = {
1508 : : {"remote", required_argument, NULL, OPT_REMOTE},
1509 : : {"unixctl", required_argument, NULL, OPT_UNIXCTL},
1510 : : #ifndef _WIN32
1511 : : {"run", required_argument, NULL, OPT_RUN},
1512 : : #endif
1513 : : {"help", no_argument, NULL, 'h'},
1514 : : {"version", no_argument, NULL, 'V'},
1515 : : DAEMON_LONG_OPTIONS,
1516 : : VLOG_LONG_OPTIONS,
1517 : : {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
1518 : : {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
1519 : : {"private-key", required_argument, NULL, 'p'},
1520 : : {"certificate", required_argument, NULL, 'c'},
1521 : : {"ca-cert", required_argument, NULL, 'C'},
1522 : : {"sync-from", required_argument, NULL, OPT_SYNC_FROM},
1523 : : {"sync-exclude-tables", required_argument, NULL, OPT_SYNC_EXCLUDE},
1524 : : {"active", no_argument, NULL, OPT_ACTIVE},
1525 : : {NULL, 0, NULL, 0},
1526 : : };
1527 : 1297 : char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
1528 : 1297 : int argc = *argcp;
1529 : 1297 : char **argv = *argvp;
1530 : :
1531 : 1297 : *sync_from = NULL;
1532 : 1297 : *sync_exclude = NULL;
1533 : 1297 : sset_init(remotes);
1534 : : for (;;) {
1535 : : int c;
1536 : :
1537 : 8288 : c = getopt_long(argc, argv, short_options, long_options, NULL);
1538 [ + + ]: 8288 : if (c == -1) {
1539 : 1297 : break;
1540 : : }
1541 : :
1542 [ + + + - : 6991 : switch (c) {
- + + - -
+ - + + -
+ - + + +
- + + + +
- - ]
1543 : : case OPT_REMOTE:
1544 : 1325 : sset_add(remotes, optarg);
1545 : 1325 : break;
1546 : :
1547 : : case OPT_UNIXCTL:
1548 : 552 : *unixctl_pathp = optarg;
1549 : 552 : break;
1550 : :
1551 : : case OPT_RUN:
1552 : 108 : *run_command = optarg;
1553 : 108 : break;
1554 : :
1555 : : case 'h':
1556 : 0 : usage();
1557 : :
1558 : : case 'V':
1559 : 0 : ovs_print_version(0, 0);
1560 : 0 : exit(EXIT_SUCCESS);
1561 : :
1562 : 1300 : VLOG_OPTION_HANDLERS
1563 : 3571 : DAEMON_OPTION_HANDLERS
1564 : :
1565 : : case 'p':
1566 : 29 : private_key_file = optarg;
1567 : 29 : break;
1568 : :
1569 : : case 'c':
1570 : 29 : certificate_file = optarg;
1571 : 29 : break;
1572 : :
1573 : : case 'C':
1574 : 29 : ca_cert_file = optarg;
1575 : 29 : bootstrap_ca_cert = false;
1576 : 29 : break;
1577 : :
1578 : : case OPT_BOOTSTRAP_CA_CERT:
1579 : 0 : ca_cert_file = optarg;
1580 : 0 : bootstrap_ca_cert = true;
1581 : 0 : break;
1582 : :
1583 : : case OPT_PEER_CA_CERT:
1584 : 1 : stream_ssl_set_peer_ca_cert_file(optarg);
1585 : 1 : break;
1586 : :
1587 : : case OPT_SYNC_FROM:
1588 : 38 : *sync_from = xstrdup(optarg);
1589 : 38 : break;
1590 : :
1591 : : case OPT_SYNC_EXCLUDE: {
1592 : 7 : char *err = set_blacklist_tables(optarg, false);
1593 [ - + ]: 7 : if (err) {
1594 : 0 : ovs_fatal(0, "%s", err);
1595 : : }
1596 : 7 : *sync_exclude = xstrdup(optarg);
1597 : 7 : break;
1598 : : }
1599 : : case OPT_ACTIVE:
1600 : 2 : *active = true;
1601 : 2 : break;
1602 : :
1603 : : case '?':
1604 : 0 : exit(EXIT_FAILURE);
1605 : :
1606 : : default:
1607 : 0 : abort();
1608 : : }
1609 : 6991 : }
1610 : 1297 : free(short_options);
1611 : :
1612 : 1297 : *argcp -= optind;
1613 : 1297 : *argvp += optind;
1614 : 1297 : }
1615 : :
1616 : : static void
1617 : 0 : usage(void)
1618 : : {
1619 : 0 : printf("%s: Open vSwitch database server\n"
1620 : : "usage: %s [OPTIONS] [DATABASE...]\n"
1621 : : "where each DATABASE is a database file in ovsdb format.\n"
1622 : : "The default DATABASE, if none is given, is\n%s/conf.db.\n",
1623 : : program_name, program_name, ovs_dbdir());
1624 : 0 : printf("\nJSON-RPC options (may be specified any number of times):\n"
1625 : : " --remote=REMOTE connect or listen to REMOTE\n");
1626 : 0 : stream_usage("JSON-RPC", true, true, true);
1627 : 0 : daemon_usage();
1628 : 0 : vlog_usage();
1629 : 0 : replication_usage();
1630 : 0 : printf("\nOther options:\n"
1631 : : " --run COMMAND run COMMAND as subprocess then exit\n"
1632 : : " --unixctl=SOCKET override default control socket name\n"
1633 : : " -h, --help display this help message\n"
1634 : : " -V, --version display version information\n");
1635 : 0 : exit(EXIT_SUCCESS);
1636 : : }
1637 : :
1638 : : static struct json *
1639 : 2622 : sset_to_json(const struct sset *sset)
1640 : : {
1641 : : struct json *array;
1642 : : const char *s;
1643 : :
1644 : 2622 : array = json_array_create_empty();
1645 [ + + ][ + + ]: 5286 : SSET_FOR_EACH (s, sset) {
[ + + ]
1646 : 2664 : json_array_add(array, json_string_create(s));
1647 : : }
1648 : 2622 : return array;
1649 : : }
1650 : :
1651 : : /* Truncates and replaces the contents of 'config_file' by a representation of
1652 : : * 'remotes' and 'db_filenames'. */
1653 : : static void
1654 : 1311 : save_config__(FILE *config_file, const struct sset *remotes,
1655 : : const struct sset *db_filenames, const char *sync_from,
1656 : : const char *sync_exclude, bool is_backup)
1657 : : {
1658 : : struct json *obj;
1659 : : char *s;
1660 : :
1661 [ - + ]: 1311 : if (ftruncate(fileno(config_file), 0) == -1) {
1662 : 0 : VLOG_FATAL("failed to truncate temporary file (%s)",
1663 : : ovs_strerror(errno));
1664 : : }
1665 : :
1666 : 1311 : obj = json_object_create();
1667 : 1311 : json_object_put(obj, "remotes", sset_to_json(remotes));
1668 : 1311 : json_object_put(obj, "db_filenames", sset_to_json(db_filenames));
1669 [ + + ]: 1311 : if (sync_from) {
1670 : 42 : json_object_put(obj, "sync_from", json_string_create(sync_from));
1671 : : }
1672 [ + + ]: 1311 : if (sync_exclude) {
1673 : 8 : json_object_put(obj, "sync_exclude",
1674 : : json_string_create(sync_exclude));
1675 : : }
1676 : 1311 : json_object_put(obj, "is_backup", json_boolean_create(is_backup));
1677 : :
1678 : 1311 : s = json_to_string(obj, 0);
1679 : 1311 : json_destroy(obj);
1680 : :
1681 [ + - ]: 1311 : if (fseek(config_file, 0, SEEK_SET) != 0
1682 [ + - ]: 1311 : || fputs(s, config_file) == EOF
1683 [ - + ]: 1311 : || fflush(config_file) == EOF) {
1684 : 0 : VLOG_FATAL("failed to write temporary file (%s)", ovs_strerror(errno));
1685 : : }
1686 : 1311 : free(s);
1687 : 1311 : }
1688 : :
1689 : : /* Truncates and replaces the contents of 'config_file' by a representation of
1690 : : * 'config'. */
1691 : : static void
1692 : 14 : save_config(struct server_config *config)
1693 : : {
1694 : : struct sset db_filenames;
1695 : : struct shash_node *node;
1696 : :
1697 : 14 : sset_init(&db_filenames);
1698 [ + + ][ - + ]: 30 : SHASH_FOR_EACH (node, config->all_dbs) {
1699 : 16 : struct db *db = node->data;
1700 : 16 : sset_add(&db_filenames, db->filename);
1701 : : }
1702 : :
1703 : 14 : save_config__(config->config_tmpfile, config->remotes, &db_filenames,
1704 : 28 : *config->sync_from, *config->sync_exclude,
1705 : 14 : *config->is_backup);
1706 : :
1707 : 14 : sset_destroy(&db_filenames);
1708 : 14 : }
1709 : :
1710 : : static void
1711 : 2520 : sset_from_json(struct sset *sset, const struct json *array)
1712 : : {
1713 : : size_t i;
1714 : :
1715 : 2520 : sset_clear(sset);
1716 : :
1717 [ - + ]: 2520 : ovs_assert(array->type == JSON_ARRAY);
1718 [ + + ]: 5080 : for (i = 0; i < array->u.array.n; i++) {
1719 : 2560 : const struct json *elem = array->u.array.elems[i];
1720 : 2560 : sset_add(sset, json_string(elem));
1721 : : }
1722 : 2520 : }
1723 : :
1724 : : /* Clears and replaces 'remotes' and 'dbnames' by a configuration read from
1725 : : * 'config_file', which must have been previously written by save_config(). */
1726 : : static void
1727 : 1260 : load_config(FILE *config_file, struct sset *remotes, struct sset *db_filenames,
1728 : : char **sync_from, char **sync_exclude, bool *is_backup)
1729 : : {
1730 : : struct json *json;
1731 : :
1732 [ - + ]: 1260 : if (fseek(config_file, 0, SEEK_SET) != 0) {
1733 : 0 : VLOG_FATAL("seek failed in temporary file (%s)", ovs_strerror(errno));
1734 : : }
1735 : 1260 : json = json_from_stream(config_file);
1736 [ - + ]: 1260 : if (json->type == JSON_STRING) {
1737 : 0 : VLOG_FATAL("reading json failed (%s)", json_string(json));
1738 : : }
1739 [ - + ]: 1260 : ovs_assert(json->type == JSON_OBJECT);
1740 : :
1741 : 1260 : sset_from_json(remotes, shash_find_data(json_object(json), "remotes"));
1742 : 1260 : sset_from_json(db_filenames,
1743 : 1260 : shash_find_data(json_object(json), "db_filenames"));
1744 : :
1745 : : struct json *string;
1746 : 1260 : string = shash_find_data(json_object(json), "sync_from");
1747 : 1260 : free(*sync_from);
1748 [ + + ]: 1260 : *sync_from = string ? xstrdup(json_string(string)) : NULL;
1749 : :
1750 : 1260 : string = shash_find_data(json_object(json), "sync_exclude");
1751 : 1260 : free(*sync_exclude);
1752 [ + + ]: 1260 : *sync_exclude = string ? xstrdup(json_string(string)) : NULL;
1753 : :
1754 : 1260 : *is_backup = json_boolean(shash_find_data(json_object(json), "is_backup"));
1755 : :
1756 : 1260 : json_destroy(json);
1757 : 1260 : }
|