Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at:
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #include <config.h>
18 : :
19 : : #include <ctype.h>
20 : : #include <errno.h>
21 : : #include <getopt.h>
22 : : #include <limits.h>
23 : : #include <signal.h>
24 : : #include <stdlib.h>
25 : : #include <string.h>
26 : : #include <unistd.h>
27 : :
28 : : #include "command-line.h"
29 : : #include "column.h"
30 : : #include "compiler.h"
31 : : #include "daemon.h"
32 : : #include "dirs.h"
33 : : #include "openvswitch/dynamic-string.h"
34 : : #include "fatal-signal.h"
35 : : #include "openvswitch/json.h"
36 : : #include "jsonrpc.h"
37 : : #include "lib/table.h"
38 : : #include "ovsdb.h"
39 : : #include "ovsdb-data.h"
40 : : #include "ovsdb-error.h"
41 : : #include "poll-loop.h"
42 : : #include "sort.h"
43 : : #include "svec.h"
44 : : #include "stream.h"
45 : : #include "stream-ssl.h"
46 : : #include "table.h"
47 : : #include "monitor.h"
48 : : #include "condition.h"
49 : : #include "timeval.h"
50 : : #include "unixctl.h"
51 : : #include "util.h"
52 : : #include "openvswitch/vlog.h"
53 : :
54 : 1812 : VLOG_DEFINE_THIS_MODULE(ovsdb_client);
55 : :
56 : : enum args_needed {
57 : : NEED_NONE, /* No JSON-RPC connection or database name needed. */
58 : : NEED_RPC, /* JSON-RPC connection needed. */
59 : : NEED_DATABASE /* JSON-RPC connection and database name needed. */
60 : : };
61 : :
62 : : struct ovsdb_client_command {
63 : : const char *name;
64 : : enum args_needed need;
65 : : int min_args;
66 : : int max_args;
67 : : void (*handler)(struct jsonrpc *rpc, const char *database,
68 : : int argc, char *argv[]);
69 : : };
70 : :
71 : : /* --timestamp: Print a timestamp before each update on "monitor" command? */
72 : : static bool timestamp;
73 : :
74 : : /* Format for table output. */
75 : : static struct table_style table_style = TABLE_STYLE_DEFAULT;
76 : :
77 : : static const struct ovsdb_client_command *get_all_commands(void);
78 : :
79 : : OVS_NO_RETURN static void usage(void);
80 : : static void parse_options(int argc, char *argv[]);
81 : : static struct jsonrpc *open_jsonrpc(const char *server);
82 : : static void fetch_dbs(struct jsonrpc *, struct svec *dbs);
83 : :
84 : : int
85 : 906 : main(int argc, char *argv[])
86 : : {
87 : : const struct ovsdb_client_command *command;
88 : : char *database;
89 : : struct jsonrpc *rpc;
90 : :
91 : 906 : ovs_cmdl_proctitle_init(argc, argv);
92 : 906 : set_program_name(argv[0]);
93 : 906 : parse_options(argc, argv);
94 : 906 : fatal_ignore_sigpipe();
95 : :
96 : 906 : daemon_become_new_user(false);
97 [ - + ]: 906 : if (optind >= argc) {
98 : 0 : ovs_fatal(0, "missing command name; use --help for help");
99 : : }
100 : :
101 : 906 : for (command = get_all_commands(); ; command++) {
102 [ - + ]: 5681 : if (!command->name) {
103 : 0 : VLOG_FATAL("unknown command '%s'; use --help for help",
104 : : argv[optind]);
105 [ + + ]: 5681 : } else if (!strcmp(command->name, argv[optind])) {
106 : 906 : break;
107 : : }
108 : 4775 : }
109 : 906 : optind++;
110 : :
111 [ + - ]: 906 : if (command->need != NEED_NONE) {
112 [ + + ]: 906 : if (argc - optind > command->min_args
113 [ + - ]: 748 : && (isalpha((unsigned char) argv[optind][0])
114 [ + + ]: 748 : && strchr(argv[optind], ':'))) {
115 : 706 : rpc = open_jsonrpc(argv[optind++]);
116 : : } else {
117 : 200 : char *sock = xasprintf("unix:%s/db.sock", ovs_rundir());
118 : 200 : rpc = open_jsonrpc(sock);
119 : 906 : free(sock);
120 : : }
121 : : } else {
122 : 0 : rpc = NULL;
123 : : }
124 : :
125 [ + + ]: 906 : if (command->need == NEED_DATABASE) {
126 : : struct svec dbs;
127 : :
128 : 167 : svec_init(&dbs);
129 : 167 : fetch_dbs(rpc, &dbs);
130 [ + + ]: 167 : if (argc - optind > command->min_args
131 [ + + ]: 79 : && svec_contains(&dbs, argv[optind])) {
132 : 77 : database = xstrdup(argv[optind++]);
133 [ + + ]: 90 : } else if (dbs.n == 1) {
134 : 89 : database = xstrdup(dbs.names[0]);
135 [ - + ]: 1 : } else if (svec_contains(&dbs, "Open_vSwitch")) {
136 : 0 : database = xstrdup("Open_vSwitch");
137 : : } else {
138 : 1 : jsonrpc_close(rpc);
139 : 1 : ovs_fatal(0, "no default database for `%s' command, please "
140 : : "specify a database name", command->name);
141 : : }
142 : 166 : svec_destroy(&dbs);
143 : : } else {
144 : 739 : database = NULL;
145 : : }
146 : :
147 [ + - ][ + + ]: 905 : if (argc - optind < command->min_args ||
148 : 905 : argc - optind > command->max_args) {
149 : 1 : free(database);
150 : 1 : VLOG_FATAL("invalid syntax for '%s' (use --help for help)",
151 : : command->name);
152 : : }
153 : :
154 : 904 : command->handler(rpc, database, argc - optind, argv + optind);
155 : :
156 : 876 : free(database);
157 : 876 : jsonrpc_close(rpc);
158 : :
159 [ - + ]: 876 : if (ferror(stdout)) {
160 : 0 : VLOG_FATAL("write to stdout failed");
161 : : }
162 [ - + ]: 876 : if (ferror(stderr)) {
163 : 0 : VLOG_FATAL("write to stderr failed");
164 : : }
165 : :
166 : 876 : return 0;
167 : : }
168 : :
169 : : static void
170 : 906 : parse_options(int argc, char *argv[])
171 : : {
172 : : enum {
173 : : OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1,
174 : : OPT_TIMESTAMP,
175 : : VLOG_OPTION_ENUMS,
176 : : DAEMON_OPTION_ENUMS,
177 : : TABLE_OPTION_ENUMS
178 : : };
179 : : static const struct option long_options[] = {
180 : : {"help", no_argument, NULL, 'h'},
181 : : {"version", no_argument, NULL, 'V'},
182 : : {"timestamp", no_argument, NULL, OPT_TIMESTAMP},
183 : : VLOG_LONG_OPTIONS,
184 : : DAEMON_LONG_OPTIONS,
185 : : #ifdef HAVE_OPENSSL
186 : : {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
187 : : STREAM_SSL_LONG_OPTIONS,
188 : : #endif
189 : : TABLE_LONG_OPTIONS,
190 : : {NULL, 0, NULL, 0},
191 : : };
192 : 906 : char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
193 : :
194 : : for (;;) {
195 : : int c;
196 : :
197 : 1413 : c = getopt_long(argc, argv, short_options, long_options, NULL);
198 [ + + ]: 1413 : if (c == -1) {
199 : 906 : break;
200 : : }
201 : :
202 [ - - + + : 507 : switch (c) {
- - + - +
+ - - - +
+ + - - +
+ + - - -
- - ]
203 : : case 'h':
204 : 0 : usage();
205 : :
206 : : case 'V':
207 : 0 : ovs_print_version(0, 0);
208 : 0 : exit(EXIT_SUCCESS);
209 : :
210 : 35 : VLOG_OPTION_HANDLERS
211 : 77 : DAEMON_OPTION_HANDLERS
212 : 86 : TABLE_OPTION_HANDLERS(&table_style)
213 : 309 : STREAM_SSL_OPTION_HANDLERS
214 : :
215 : : case OPT_BOOTSTRAP_CA_CERT:
216 : 0 : stream_ssl_set_ca_cert_file(optarg, true);
217 : 0 : break;
218 : :
219 : : case OPT_TIMESTAMP:
220 : 0 : timestamp = true;
221 : 0 : break;
222 : :
223 : : case '?':
224 : 0 : exit(EXIT_FAILURE);
225 : :
226 : : case 0:
227 : : /* getopt_long() already set the value for us. */
228 : 0 : break;
229 : :
230 : : default:
231 : 0 : abort();
232 : : }
233 : 507 : }
234 : 906 : free(short_options);
235 : 906 : }
236 : :
237 : : static void
238 : 0 : usage(void)
239 : : {
240 : 0 : printf("%s: Open vSwitch database JSON-RPC client\n"
241 : : "usage: %s [OPTIONS] COMMAND [ARG...]\n"
242 : : "\nValid commands are:\n"
243 : : "\n list-dbs [SERVER]\n"
244 : : " list databases available on SERVER\n"
245 : : "\n get-schema [SERVER] [DATABASE]\n"
246 : : " retrieve schema for DATABASE from SERVER\n"
247 : : "\n get-schema-version [SERVER] [DATABASE]\n"
248 : : " retrieve schema for DATABASE from SERVER and report only its\n"
249 : : " version number on stdout\n"
250 : : "\n list-tables [SERVER] [DATABASE]\n"
251 : : " list tables for DATABASE on SERVER\n"
252 : : "\n list-columns [SERVER] [DATABASE] [TABLE]\n"
253 : : " list columns in TABLE (or all tables) in DATABASE on SERVER\n"
254 : : "\n transact [SERVER] TRANSACTION\n"
255 : : " run TRANSACTION (a JSON array of operations) on SERVER\n"
256 : : " and print the results as JSON on stdout\n"
257 : : "\n monitor [SERVER] [DATABASE] TABLE [COLUMN,...]...\n"
258 : : " monitor contents of COLUMNs in TABLE in DATABASE on SERVER.\n"
259 : : " COLUMNs may include !initial, !insert, !delete, !modify\n"
260 : : " to avoid seeing the specified kinds of changes.\n"
261 : : "\n monitor-cond [SERVER] [DATABASE] CONDITION TABLE [COLUMN,...]...\n"
262 : : " monitor contents that match CONDITION of COLUMNs in TABLE in\n"
263 : : " DATABASE on SERVER.\n"
264 : : " COLUMNs may include !initial, !insert, !delete, !modify\n"
265 : : " to avoid seeing the specified kinds of changes.\n"
266 : : "\n monitor [SERVER] [DATABASE] ALL\n"
267 : : " monitor all changes to all columns in all tables\n"
268 : : " in DATBASE on SERVER.\n"
269 : : "\n dump [SERVER] [DATABASE]\n"
270 : : " dump contents of DATABASE on SERVER to stdout\n"
271 : : "\n lock [SERVER] LOCK\n"
272 : : " create or wait for LOCK in SERVER\n"
273 : : "\n steal [SERVER] LOCK\n"
274 : : " steal LOCK from SERVER\n"
275 : : "\n unlock [SERVER] LOCK\n"
276 : : " unlock LOCK from SERVER\n"
277 : : "\nThe default SERVER is unix:%s/db.sock.\n"
278 : : "The default DATABASE is Open_vSwitch.\n",
279 : : program_name, program_name, ovs_rundir());
280 : 0 : stream_usage("SERVER", true, true, true);
281 : 0 : printf("\nOutput formatting options:\n"
282 : : " -f, --format=FORMAT set output formatting to FORMAT\n"
283 : : " (\"table\", \"html\", \"csv\", "
284 : : "or \"json\")\n"
285 : : " --no-headings omit table heading row\n"
286 : : " --pretty pretty-print JSON in output\n"
287 : : " --timestamp timestamp \"monitor\" output");
288 : 0 : daemon_usage();
289 : 0 : vlog_usage();
290 : 0 : printf("\nOther options:\n"
291 : : " -h, --help display this help message\n"
292 : : " -V, --version display version information\n");
293 : 0 : exit(EXIT_SUCCESS);
294 : : }
295 : :
296 : : static void
297 : 1164 : check_txn(int error, struct jsonrpc_msg **reply_)
298 : : {
299 : 1164 : struct jsonrpc_msg *reply = *reply_;
300 : :
301 [ - + ]: 1164 : if (error) {
302 : 0 : ovs_fatal(error, "transaction failed");
303 : : }
304 : :
305 [ - + ]: 1164 : if (reply->error) {
306 : 0 : ovs_fatal(error, "transaction returned error: %s",
307 : 0 : json_to_string(reply->error, table_style.json_flags));
308 : : }
309 : 1164 : }
310 : :
311 : : static struct json *
312 : 739 : parse_json(const char *s)
313 : : {
314 : 739 : struct json *json = json_from_string(s);
315 [ - + ]: 739 : if (json->type == JSON_STRING) {
316 : 0 : ovs_fatal(0, "\"%s\": %s", s, json->u.string);
317 : : }
318 : 739 : return json;
319 : : }
320 : :
321 : : static struct jsonrpc *
322 : 906 : open_jsonrpc(const char *server)
323 : : {
324 : : struct stream *stream;
325 : : int error;
326 : :
327 : 906 : error = stream_open_block(jsonrpc_stream_open(server, &stream,
328 : : DSCP_DEFAULT), &stream);
329 [ - + ]: 906 : if (error == EAFNOSUPPORT) {
330 : : struct pstream *pstream;
331 : :
332 : 0 : error = jsonrpc_pstream_open(server, &pstream, DSCP_DEFAULT);
333 [ # # ]: 0 : if (error) {
334 : 0 : ovs_fatal(error, "failed to connect or listen to \"%s\"", server);
335 : : }
336 : :
337 [ # # ]: 0 : VLOG_INFO("%s: waiting for connection...", server);
338 : 0 : error = pstream_accept_block(pstream, &stream);
339 [ # # ]: 0 : if (error) {
340 : 0 : ovs_fatal(error, "failed to accept connection on \"%s\"", server);
341 : : }
342 : :
343 : 0 : pstream_close(pstream);
344 [ - + ]: 906 : } else if (error) {
345 : 0 : ovs_fatal(error, "failed to connect to \"%s\"", server);
346 : : }
347 : :
348 : 906 : return jsonrpc_open(stream);
349 : : }
350 : :
351 : : static void
352 : 743 : print_json(struct json *json)
353 : : {
354 : 743 : char *string = json_to_string(json, table_style.json_flags);
355 : 743 : fputs(string, stdout);
356 : 743 : free(string);
357 : 743 : }
358 : :
359 : : static void
360 : 0 : print_and_free_json(struct json *json)
361 : : {
362 : 0 : print_json(json);
363 : 0 : json_destroy(json);
364 : 0 : }
365 : :
366 : : static void
367 : 645 : check_ovsdb_error(struct ovsdb_error *error)
368 : : {
369 [ - + ]: 645 : if (error) {
370 : 0 : ovs_fatal(0, "%s", ovsdb_error_to_string(error));
371 : : }
372 : 645 : }
373 : :
374 : : static struct ovsdb_schema *
375 : 171 : fetch_schema(struct jsonrpc *rpc, const char *database)
376 : : {
377 : : struct jsonrpc_msg *request, *reply;
378 : : struct ovsdb_schema *schema;
379 : :
380 : 171 : request = jsonrpc_create_request("get_schema",
381 : : json_array_create_1(
382 : : json_string_create(database)),
383 : : NULL);
384 : 171 : check_txn(jsonrpc_transact_block(rpc, request, &reply), &reply);
385 : 171 : check_ovsdb_error(ovsdb_schema_from_json(reply->result, &schema));
386 : 171 : jsonrpc_msg_destroy(reply);
387 : :
388 : 171 : return schema;
389 : : }
390 : :
391 : : static void
392 : 168 : fetch_dbs(struct jsonrpc *rpc, struct svec *dbs)
393 : : {
394 : : struct jsonrpc_msg *request, *reply;
395 : : size_t i;
396 : :
397 : 168 : request = jsonrpc_create_request("list_dbs", json_array_create_empty(),
398 : : NULL);
399 : :
400 : 168 : check_txn(jsonrpc_transact_block(rpc, request, &reply), &reply);
401 [ - + ]: 168 : if (reply->result->type != JSON_ARRAY) {
402 : 0 : ovs_fatal(0, "list_dbs response is not array");
403 : : }
404 : :
405 [ + + ]: 338 : for (i = 0; i < reply->result->u.array.n; i++) {
406 : 170 : const struct json *name = reply->result->u.array.elems[i];
407 : :
408 [ - + ]: 170 : if (name->type != JSON_STRING) {
409 : 0 : ovs_fatal(0, "list_dbs response %"PRIuSIZE" is not string", i);
410 : : }
411 : 170 : svec_add(dbs, name->u.string);
412 : : }
413 : 168 : jsonrpc_msg_destroy(reply);
414 : 168 : svec_sort(dbs);
415 : 168 : }
416 : :
417 : : static void
418 : 1 : do_list_dbs(struct jsonrpc *rpc, const char *database OVS_UNUSED,
419 : : int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
420 : : {
421 : : const char *db_name;
422 : : struct svec dbs;
423 : : size_t i;
424 : :
425 : 1 : svec_init(&dbs);
426 : 1 : fetch_dbs(rpc, &dbs);
427 [ + + ][ + + ]: 3 : SVEC_FOR_EACH (i, db_name, &dbs) {
428 : 2 : puts(db_name);
429 : : }
430 : 1 : svec_destroy(&dbs);
431 : 1 : }
432 : :
433 : : static void
434 : 0 : do_get_schema(struct jsonrpc *rpc, const char *database,
435 : : int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
436 : : {
437 : 0 : struct ovsdb_schema *schema = fetch_schema(rpc, database);
438 : 0 : print_and_free_json(ovsdb_schema_to_json(schema));
439 : 0 : ovsdb_schema_destroy(schema);
440 : 0 : }
441 : :
442 : : static void
443 : 2 : do_get_schema_version(struct jsonrpc *rpc, const char *database,
444 : : int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
445 : : {
446 : 2 : struct ovsdb_schema *schema = fetch_schema(rpc, database);
447 : 2 : puts(schema->version);
448 : 2 : ovsdb_schema_destroy(schema);
449 : 2 : }
450 : :
451 : : static void
452 : 27 : do_list_tables(struct jsonrpc *rpc, const char *database,
453 : : int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
454 : : {
455 : : struct ovsdb_schema *schema;
456 : : struct shash_node *node;
457 : : struct table t;
458 : :
459 : 27 : schema = fetch_schema(rpc, database);
460 : 27 : table_init(&t);
461 : 27 : table_add_column(&t, "Table");
462 [ + + ][ - + ]: 418 : SHASH_FOR_EACH (node, &schema->tables) {
463 : 391 : struct ovsdb_table_schema *ts = node->data;
464 : :
465 : 391 : table_add_row(&t);
466 : 391 : table_add_cell(&t)->text = xstrdup(ts->name);
467 : : }
468 : 27 : ovsdb_schema_destroy(schema);
469 : 27 : table_print(&t, &table_style);
470 : 27 : table_destroy(&t);
471 : 27 : }
472 : :
473 : : static void
474 : 20 : do_list_columns(struct jsonrpc *rpc, const char *database,
475 : : int argc OVS_UNUSED, char *argv[])
476 : : {
477 : 20 : const char *table_name = argv[0];
478 : : struct ovsdb_schema *schema;
479 : : struct shash_node *table_node;
480 : : struct table t;
481 : :
482 : 20 : schema = fetch_schema(rpc, database);
483 : 20 : table_init(&t);
484 [ - + ]: 20 : if (!table_name) {
485 : 0 : table_add_column(&t, "Table");
486 : : }
487 : 20 : table_add_column(&t, "Column");
488 : 20 : table_add_column(&t, "Type");
489 [ + + ][ - + ]: 340 : SHASH_FOR_EACH (table_node, &schema->tables) {
490 : 320 : struct ovsdb_table_schema *ts = table_node->data;
491 : :
492 [ + - ][ + + ]: 320 : if (!table_name || !strcmp(table_name, ts->name)) {
493 : : struct shash_node *column_node;
494 : :
495 [ + + ][ - + ]: 476 : SHASH_FOR_EACH (column_node, &ts->columns) {
496 : 456 : const struct ovsdb_column *column = column_node->data;
497 : :
498 : 456 : table_add_row(&t);
499 [ - + ]: 456 : if (!table_name) {
500 : 0 : table_add_cell(&t)->text = xstrdup(ts->name);
501 : : }
502 : 456 : table_add_cell(&t)->text = xstrdup(column->name);
503 : 456 : table_add_cell(&t)->json = ovsdb_type_to_json(&column->type);
504 : : }
505 : : }
506 : : }
507 : 20 : ovsdb_schema_destroy(schema);
508 : 20 : table_print(&t, &table_style);
509 : 20 : table_destroy(&t);
510 : 20 : }
511 : :
512 : : static void
513 : 732 : do_transact(struct jsonrpc *rpc, const char *database OVS_UNUSED,
514 : : int argc OVS_UNUSED, char *argv[])
515 : : {
516 : : struct jsonrpc_msg *request, *reply;
517 : : struct json *transaction;
518 : :
519 : 732 : transaction = parse_json(argv[0]);
520 : :
521 : 732 : request = jsonrpc_create_request("transact", transaction, NULL);
522 : 732 : check_txn(jsonrpc_transact_block(rpc, request, &reply), &reply);
523 : 732 : print_json(reply->result);
524 : 732 : putchar('\n');
525 : 732 : jsonrpc_msg_destroy(reply);
526 : 732 : }
527 : :
528 : : /* "monitor" command. */
529 : :
530 : : struct monitored_table {
531 : : struct ovsdb_table_schema *table;
532 : : struct ovsdb_column_set columns;
533 : : };
534 : :
535 : : static void
536 : 465 : monitor_print_row(struct json *row, const char *type, const char *uuid,
537 : : const struct ovsdb_column_set *columns, struct table *t)
538 : : {
539 : : size_t i;
540 : :
541 [ - + ]: 465 : if (!row) {
542 : 0 : ovs_error(0, "missing %s row", type);
543 : 0 : return;
544 [ - + ]: 465 : } else if (row->type != JSON_OBJECT) {
545 : 0 : ovs_error(0, "<row> is not object");
546 : 0 : return;
547 : : }
548 : :
549 : 465 : table_add_row(t);
550 : 465 : table_add_cell(t)->text = xstrdup(uuid);
551 : 465 : table_add_cell(t)->text = xstrdup(type);
552 [ + + ]: 11912 : for (i = 0; i < columns->n_columns; i++) {
553 : 11447 : const struct ovsdb_column *column = columns->columns[i];
554 : 11447 : struct json *value = shash_find_data(json_object(row), column->name);
555 : 11447 : struct cell *cell = table_add_cell(t);
556 [ + + ]: 11447 : if (value) {
557 : 11386 : cell->json = json_clone(value);
558 : 11386 : cell->type = &column->type;
559 : : }
560 : : }
561 : : }
562 : :
563 : : static void
564 : 36 : monitor_print_table(struct json *table_update,
565 : : const struct monitored_table *mt, char *caption,
566 : : bool initial)
567 : : {
568 : 36 : const struct ovsdb_table_schema *table = mt->table;
569 : 36 : const struct ovsdb_column_set *columns = &mt->columns;
570 : : struct shash_node *node;
571 : : struct table t;
572 : : size_t i;
573 : :
574 [ - + ]: 36 : if (table_update->type != JSON_OBJECT) {
575 : 0 : ovs_error(0, "<table-update> for table %s is not object", table->name);
576 : 0 : return;
577 : : }
578 : :
579 : 36 : table_init(&t);
580 : 36 : table_set_timestamp(&t, timestamp);
581 : 36 : table_set_caption(&t, caption);
582 : :
583 : 36 : table_add_column(&t, "row");
584 : 36 : table_add_column(&t, "action");
585 [ + + ]: 346 : for (i = 0; i < columns->n_columns; i++) {
586 : 310 : table_add_column(&t, "%s", columns->columns[i]->name);
587 : : }
588 [ + + ][ - + ]: 476 : SHASH_FOR_EACH (node, json_object(table_update)) {
589 : 440 : struct json *row_update = node->data;
590 : : struct json *old, *new;
591 : :
592 [ - + ]: 440 : if (row_update->type != JSON_OBJECT) {
593 : 0 : ovs_error(0, "<row-update> is not object");
594 : 0 : continue;
595 : : }
596 : 440 : old = shash_find_data(json_object(row_update), "old");
597 : 440 : new = shash_find_data(json_object(row_update), "new");
598 [ + + ]: 440 : if (initial) {
599 : 12 : monitor_print_row(new, "initial", node->name, columns, &t);
600 [ + + ]: 428 : } else if (!old) {
601 : 211 : monitor_print_row(new, "insert", node->name, columns, &t);
602 [ + + ]: 217 : } else if (!new) {
603 : 209 : monitor_print_row(old, "delete", node->name, columns, &t);
604 : : } else {
605 : 8 : monitor_print_row(old, "old", node->name, columns, &t);
606 : 8 : monitor_print_row(new, "new", "", columns, &t);
607 : : }
608 : : }
609 : 36 : table_print(&t, &table_style);
610 : 36 : table_destroy(&t);
611 : : }
612 : :
613 : : static void
614 : 34 : monitor_print(struct json *table_updates,
615 : : const struct monitored_table *mts, size_t n_mts,
616 : : bool initial)
617 : : {
618 : : size_t i;
619 : :
620 [ - + ]: 34 : if (table_updates->type != JSON_OBJECT) {
621 : 0 : ovs_error(0, "<table-updates> is not object");
622 : 0 : return;
623 : : }
624 : :
625 [ + + ]: 143 : for (i = 0; i < n_mts; i++) {
626 : 109 : const struct monitored_table *mt = &mts[i];
627 : 109 : struct json *table_update = shash_find_data(json_object(table_updates),
628 : 109 : mt->table->name);
629 [ + + ]: 109 : if (table_update) {
630 [ + + ]: 36 : monitor_print_table(table_update, mt,
631 : 11 : n_mts > 1 ? xstrdup(mt->table->name) : NULL,
632 : : initial);
633 : : }
634 : : }
635 : : }
636 : :
637 : : static void
638 : 22 : monitor2_print_row(struct json *row, const char *type, const char *uuid,
639 : : const struct ovsdb_column_set *columns, struct table *t)
640 : : {
641 [ + + ]: 22 : if (!strcmp(type, "delete")) {
642 [ - + ]: 5 : if (row->type != JSON_NULL) {
643 : 0 : ovs_error(0, "delete method does not expect <row>");
644 : 0 : return;
645 : : }
646 : :
647 : 5 : table_add_row(t);
648 : 5 : table_add_cell(t)->text = xstrdup(uuid);
649 : 5 : table_add_cell(t)->text = xstrdup(type);
650 : : } else {
651 [ + - ][ - + ]: 17 : if (!row || row->type != JSON_OBJECT) {
652 : 0 : ovs_error(0, "<row> is not object");
653 : 0 : return;
654 : : }
655 : 17 : monitor_print_row(row, type, uuid, columns, t);
656 : : }
657 : : }
658 : :
659 : : static void
660 : 15 : monitor2_print_table(struct json *table_update2,
661 : : const struct monitored_table *mt, char *caption)
662 : : {
663 : 15 : const struct ovsdb_table_schema *table = mt->table;
664 : 15 : const struct ovsdb_column_set *columns = &mt->columns;
665 : : struct shash_node *node;
666 : : struct table t;
667 : :
668 [ - + ]: 15 : if (table_update2->type != JSON_OBJECT) {
669 : 0 : ovs_error(0, "<table-update> for table %s is not object", table->name);
670 : 0 : return;
671 : : }
672 : :
673 : 15 : table_init(&t);
674 : 15 : table_set_timestamp(&t, timestamp);
675 : 15 : table_set_caption(&t, caption);
676 : :
677 : 15 : table_add_column(&t, "row");
678 : 15 : table_add_column(&t, "action");
679 [ + + ]: 56 : for (size_t i = 0; i < columns->n_columns; i++) {
680 : 41 : table_add_column(&t, "%s", columns->columns[i]->name);
681 : : }
682 [ + + ][ - + ]: 37 : SHASH_FOR_EACH (node, json_object(table_update2)) {
683 : 22 : struct json *row_update2 = node->data;
684 : : const char *operation;
685 : : struct json *row;
686 : 22 : const char *ops[] = {"delete", "initial", "modify", "insert"};
687 : :
688 [ - + ]: 22 : if (row_update2->type != JSON_OBJECT) {
689 : 0 : ovs_error(0, "<row-update2> is not object");
690 : 0 : continue;
691 : : }
692 : :
693 : : /* row_update2 contains one of objects indexed by ops[] */
694 [ + - ]: 77 : for (int i = 0; i < ARRAY_SIZE(ops); i++) {
695 : 55 : operation = ops[i];
696 : 55 : row = shash_find_data(json_object(row_update2), operation);
697 : :
698 [ + + ]: 55 : if (row) {
699 : 22 : monitor2_print_row(row, operation, node->name, columns, &t);
700 : 22 : break;
701 : : }
702 : : }
703 : : }
704 : 15 : table_print(&t, &table_style);
705 : 15 : table_destroy(&t);
706 : : }
707 : :
708 : : static void
709 : 17 : monitor2_print(struct json *table_updates2,
710 : : const struct monitored_table *mts, size_t n_mts)
711 : : {
712 : : size_t i;
713 : :
714 [ - + ]: 17 : if (table_updates2->type != JSON_OBJECT) {
715 : 0 : ovs_error(0, "<table-updates2> is not object");
716 : 0 : return;
717 : : }
718 : :
719 [ + + ]: 34 : for (i = 0; i < n_mts; i++) {
720 : 17 : const struct monitored_table *mt = &mts[i];
721 : 17 : struct json *table_update = shash_find_data(
722 : 17 : json_object(table_updates2),
723 : 17 : mt->table->name);
724 [ + + ]: 17 : if (table_update) {
725 [ - + ]: 15 : monitor2_print_table(table_update, mt,
726 : 0 : n_mts > 1 ? xstrdup(mt->table->name) : NULL);
727 : : }
728 : : }
729 : : }
730 : :
731 : : static void
732 : 246 : add_column(const char *server, const struct ovsdb_column *column,
733 : : struct ovsdb_column_set *columns, struct json *columns_json)
734 : : {
735 [ - + ]: 246 : if (ovsdb_column_set_contains(columns, column->index)) {
736 : 0 : ovs_fatal(0, "%s: column \"%s\" mentioned multiple times",
737 : : server, column->name);
738 : : }
739 : 246 : ovsdb_column_set_add(columns, column);
740 : 246 : json_array_add(columns_json, json_string_create(column->name));
741 : 246 : }
742 : :
743 : : static struct json *
744 : 37 : parse_monitor_columns(char *arg, const char *server, const char *database,
745 : : const struct ovsdb_table_schema *table,
746 : : struct ovsdb_column_set *columns)
747 : : {
748 : : bool initial, insert, delete, modify;
749 : : struct json *mr, *columns_json;
750 : 37 : char *save_ptr = NULL;
751 : : char *token;
752 : :
753 : 37 : mr = json_object_create();
754 : 37 : columns_json = json_array_create_empty();
755 : 37 : json_object_put(mr, "columns", columns_json);
756 : :
757 : 37 : initial = insert = delete = modify = true;
758 [ + + ]: 56 : for (token = strtok_r(arg, ",", &save_ptr); token != NULL;
759 : 19 : token = strtok_r(NULL, ",", &save_ptr)) {
760 [ + + ]: 19 : if (!strcmp(token, "!initial")) {
761 : 5 : initial = false;
762 [ + + ]: 14 : } else if (!strcmp(token, "!insert")) {
763 : 4 : insert = false;
764 [ + + ]: 10 : } else if (!strcmp(token, "!delete")) {
765 : 4 : delete = false;
766 [ + + ]: 6 : } else if (!strcmp(token, "!modify")) {
767 : 5 : modify = false;
768 : : } else {
769 : : const struct ovsdb_column *column;
770 : :
771 : 1 : column = ovsdb_table_schema_get_column(table, token);
772 [ - + ]: 1 : if (!column) {
773 : 0 : ovs_fatal(0, "%s: table \"%s\" in %s does not have a "
774 : : "column named \"%s\"",
775 : : server, table->name, database, token);
776 : : }
777 : 1 : add_column(server, column, columns, columns_json);
778 : : }
779 : : }
780 : :
781 [ + + ]: 37 : if (columns_json->u.array.n == 0) {
782 : : const struct shash_node **nodes;
783 : : size_t i, n;
784 : :
785 : 36 : n = shash_count(&table->columns);
786 : 36 : nodes = shash_sort(&table->columns);
787 [ + + ]: 317 : for (i = 0; i < n; i++) {
788 : 281 : const struct ovsdb_column *column = nodes[i]->data;
789 [ + + ]: 281 : if (column->index != OVSDB_COL_UUID
790 [ + + ]: 245 : && column->index != OVSDB_COL_VERSION) {
791 : 209 : add_column(server, column, columns, columns_json);
792 : : }
793 : : }
794 : 36 : free(nodes);
795 : :
796 : 36 : add_column(server, ovsdb_table_schema_get_column(table, "_version"),
797 : : columns, columns_json);
798 : : }
799 : :
800 [ + + ][ + + ]: 37 : if (!initial || !insert || !delete || !modify) {
[ + - ][ - + ]
801 : 6 : struct json *select = json_object_create();
802 : 6 : json_object_put(select, "initial", json_boolean_create(initial));
803 : 6 : json_object_put(select, "insert", json_boolean_create(insert));
804 : 6 : json_object_put(select, "delete", json_boolean_create(delete));
805 : 6 : json_object_put(select, "modify", json_boolean_create(modify));
806 : 6 : json_object_put(mr, "select", select);
807 : : }
808 : :
809 : 37 : return mr;
810 : : }
811 : :
812 : : static void
813 : 1 : ovsdb_client_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
814 : : const char *argv[] OVS_UNUSED, void *exiting_)
815 : : {
816 : 1 : bool *exiting = exiting_;
817 : 1 : *exiting = true;
818 : 1 : unixctl_command_reply(conn, NULL);
819 : 1 : }
820 : :
821 : : static void
822 : 1 : ovsdb_client_block(struct unixctl_conn *conn, int argc OVS_UNUSED,
823 : : const char *argv[] OVS_UNUSED, void *blocked_)
824 : : {
825 : 1 : bool *blocked = blocked_;
826 : :
827 [ + - ]: 1 : if (!*blocked) {
828 : 1 : *blocked = true;
829 : 1 : unixctl_command_reply(conn, NULL);
830 : : } else {
831 : 0 : unixctl_command_reply(conn, "already blocking");
832 : : }
833 : 1 : }
834 : :
835 : : static void
836 : 1 : ovsdb_client_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED,
837 : : const char *argv[] OVS_UNUSED, void *blocked_)
838 : : {
839 : 1 : bool *blocked = blocked_;
840 : :
841 [ + - ]: 1 : if (*blocked) {
842 : 1 : *blocked = false;
843 : 1 : unixctl_command_reply(conn, NULL);
844 : : } else {
845 : 0 : unixctl_command_reply(conn, "already unblocked");
846 : : }
847 : 1 : }
848 : :
849 : : static void
850 : 4 : ovsdb_client_cond_change(struct unixctl_conn *conn, int argc OVS_UNUSED,
851 : : const char *argv[], void *rpc_)
852 : : {
853 : 4 : struct jsonrpc *rpc = rpc_;
854 : 4 : struct json *monitor_cond_update_requests = json_object_create();
855 : 4 : struct json *monitor_cond_update_request = json_object_create();
856 : : struct json *params;
857 : : struct jsonrpc_msg *request;
858 : :
859 : 4 : json_object_put(monitor_cond_update_request, "where",
860 : 4 : json_from_string(argv[2]));
861 : 4 : json_object_put(monitor_cond_update_requests,
862 : 4 : argv[1],
863 : : json_array_create_1(monitor_cond_update_request));
864 : :
865 : 4 : params = json_array_create_3(json_null_create(),json_null_create(),
866 : : monitor_cond_update_requests);
867 : :
868 : 4 : request = jsonrpc_create_request("monitor_cond_change", params, NULL);
869 : 4 : jsonrpc_send(rpc, request);
870 : :
871 [ - + ]: 4 : VLOG_DBG("cond change %s %s", argv[1], argv[2]);
872 : 4 : unixctl_command_reply(conn, "condiiton changed");
873 : 4 : }
874 : :
875 : : static void
876 : 37 : add_monitored_table(int argc, char *argv[],
877 : : const char *server, const char *database,
878 : : struct json *condition,
879 : : struct ovsdb_table_schema *table,
880 : : struct json *monitor_requests,
881 : : struct monitored_table **mts,
882 : : size_t *n_mts, size_t *allocated_mts)
883 : : {
884 : : struct json *monitor_request_array, *mr;
885 : : struct monitored_table *mt;
886 : :
887 [ + + ]: 37 : if (*n_mts >= *allocated_mts) {
888 : 26 : *mts = x2nrealloc(*mts, allocated_mts, sizeof **mts);
889 : : }
890 : 37 : mt = &(*mts)[(*n_mts)++];
891 : 37 : mt->table = table;
892 : 37 : ovsdb_column_set_init(&mt->columns);
893 : :
894 : 37 : monitor_request_array = json_array_create_empty();
895 [ + + ]: 37 : if (argc > 1) {
896 : : int i;
897 : :
898 [ + + ]: 14 : for (i = 1; i < argc; i++) {
899 : 7 : mr = parse_monitor_columns(argv[i], server, database, table,
900 : : &mt->columns);
901 [ + - ][ + + ]: 7 : if (i == 1 && condition) {
902 : 3 : json_object_put(mr, "where", condition);
903 : : }
904 : 7 : json_array_add(monitor_request_array, mr);
905 : : }
906 : : } else {
907 : : /* Allocate a writable empty string since parse_monitor_columns()
908 : : * is going to strtok() it and that's risky with literal "". */
909 : 30 : char empty[] = "";
910 : :
911 : 30 : mr = parse_monitor_columns(empty, server, database,
912 : : table, &mt->columns);
913 [ + + ]: 30 : if (condition) {
914 : 4 : json_object_put(mr, "where", condition);
915 : : }
916 : 30 : json_array_add(monitor_request_array, mr);
917 : : }
918 : :
919 : 37 : json_object_put(monitor_requests, table->name, monitor_request_array);
920 : 37 : }
921 : :
922 : : static void
923 : 1 : destroy_monitored_table(struct monitored_table *mts, size_t n)
924 : : {
925 : : int i;
926 : :
927 [ + + ]: 17 : for (i = 0; i < n; i++) {
928 : 16 : struct monitored_table *mt = &mts[i];
929 : 16 : ovsdb_column_set_destroy(&mt->columns);
930 : : }
931 : :
932 : 1 : free(mts);
933 : 1 : }
934 : :
935 : : static void
936 : 23 : do_monitor__(struct jsonrpc *rpc, const char *database,
937 : : enum ovsdb_monitor_version version,
938 : : int argc, char *argv[], struct json *condition)
939 : : {
940 : 23 : const char *server = jsonrpc_get_name(rpc);
941 : 23 : const char *table_name = argv[0];
942 : : struct unixctl_server *unixctl;
943 : : struct ovsdb_schema *schema;
944 : : struct jsonrpc_msg *request;
945 : : struct json *monitor, *monitor_requests, *request_id;
946 : 23 : bool exiting = false;
947 : 23 : bool blocked = false;
948 : :
949 : : struct monitored_table *mts;
950 : : size_t n_mts, allocated_mts;
951 : :
952 [ - + ]: 23 : ovs_assert(version < OVSDB_MONITOR_VERSION_MAX);
953 : :
954 : 23 : daemon_save_fd(STDOUT_FILENO);
955 : 23 : daemonize_start(false);
956 [ + - ]: 22 : if (get_detach()) {
957 : : int error;
958 : :
959 : 22 : error = unixctl_server_create(NULL, &unixctl);
960 [ - + ]: 22 : if (error) {
961 : 0 : ovs_fatal(error, "failed to create unixctl server");
962 : : }
963 : :
964 : 22 : unixctl_command_register("exit", "", 0, 0,
965 : : ovsdb_client_exit, &exiting);
966 : 22 : unixctl_command_register("ovsdb-client/block", "", 0, 0,
967 : : ovsdb_client_block, &blocked);
968 : 22 : unixctl_command_register("ovsdb-client/unblock", "", 0, 0,
969 : : ovsdb_client_unblock, &blocked);
970 : 22 : unixctl_command_register("ovsdb-client/cond_change", "TABLE COND", 2, 2,
971 : : ovsdb_client_cond_change, rpc);
972 : : } else {
973 : 0 : unixctl = NULL;
974 : : }
975 : :
976 : 22 : schema = fetch_schema(rpc, database);
977 : :
978 : 22 : monitor_requests = json_object_create();
979 : :
980 : 22 : mts = NULL;
981 : 22 : n_mts = allocated_mts = 0;
982 [ + + ]: 22 : if (strcmp(table_name, "ALL")) {
983 : : struct ovsdb_table_schema *table;
984 : :
985 : 21 : table = shash_find_data(&schema->tables, table_name);
986 [ - + ]: 21 : if (!table) {
987 : 0 : ovs_fatal(0, "%s: %s does not have a table named \"%s\"",
988 : : server, database, table_name);
989 : : }
990 : :
991 : 21 : add_monitored_table(argc, argv, server, database, condition, table,
992 : : monitor_requests, &mts, &n_mts, &allocated_mts);
993 : : } else {
994 : 1 : size_t n = shash_count(&schema->tables);
995 : 1 : const struct shash_node **nodes = shash_sort(&schema->tables);
996 : : size_t i;
997 : :
998 [ - + ]: 1 : if (condition) {
999 : 0 : ovs_fatal(0, "ALL tables are not allowed with condition");
1000 : : }
1001 : :
1002 [ + + ]: 17 : for (i = 0; i < n; i++) {
1003 : 16 : struct ovsdb_table_schema *table = nodes[i]->data;
1004 : :
1005 : 16 : add_monitored_table(argc, argv, server, database, NULL, table,
1006 : : monitor_requests,
1007 : : &mts, &n_mts, &allocated_mts);
1008 : : }
1009 : 1 : free(nodes);
1010 : : }
1011 : :
1012 : 22 : monitor = json_array_create_3(json_string_create(database),
1013 : : json_null_create(), monitor_requests);
1014 : 22 : const char *method = version == OVSDB_MONITOR_V2 ? "monitor_cond"
1015 [ + + ]: 22 : : "monitor";
1016 : :
1017 : 22 : request = jsonrpc_create_request(method, monitor, NULL);
1018 : 22 : request_id = json_clone(request->id);
1019 : 22 : jsonrpc_send(rpc, request);
1020 : :
1021 : : for (;;) {
1022 : 117 : unixctl_server_run(unixctl);
1023 [ + + ]: 172 : while (!blocked) {
1024 : : struct jsonrpc_msg *msg;
1025 : : int error;
1026 : :
1027 : 169 : error = jsonrpc_recv(rpc, &msg);
1028 [ + + ]: 169 : if (error == EAGAIN) {
1029 : 93 : break;
1030 [ + + ]: 76 : } else if (error) {
1031 : 21 : ovs_fatal(error, "%s: receive failed", server);
1032 : : }
1033 : :
1034 [ - + ][ # # ]: 55 : if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
1035 : 0 : jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params),
1036 : 0 : msg->id));
1037 [ + + ]: 55 : } else if (msg->type == JSONRPC_REPLY
1038 [ + + ]: 26 : && json_equal(msg->id, request_id)) {
1039 [ + + - ]: 22 : switch(version) {
1040 : : case OVSDB_MONITOR_V1:
1041 : 15 : monitor_print(msg->result, mts, n_mts, true);
1042 : 15 : break;
1043 : : case OVSDB_MONITOR_V2:
1044 : 7 : monitor2_print(msg->result, mts, n_mts);
1045 : 7 : break;
1046 : : case OVSDB_MONITOR_VERSION_MAX:
1047 : : default:
1048 : 0 : OVS_NOT_REACHED();
1049 : : }
1050 : 22 : fflush(stdout);
1051 : 22 : daemonize_complete();
1052 [ + + ]: 33 : } else if (msg->type == JSONRPC_NOTIFY
1053 [ + + ]: 48 : && !strcmp(msg->method, "update")) {
1054 : 19 : struct json *params = msg->params;
1055 [ + - ]: 19 : if (params->type == JSON_ARRAY
1056 [ + - ]: 19 : && params->u.array.n == 2
1057 [ + - ]: 19 : && params->u.array.elems[0]->type == JSON_NULL) {
1058 : 19 : monitor_print(params->u.array.elems[1], mts, n_mts, false);
1059 : 19 : fflush(stdout);
1060 : : }
1061 [ + + ]: 14 : } else if (msg->type == JSONRPC_NOTIFY
1062 [ + - ]: 10 : && version == OVSDB_MONITOR_V2
1063 [ + - ]: 10 : && !strcmp(msg->method, "update2")) {
1064 : 10 : struct json *params = msg->params;
1065 [ + - ]: 10 : if (params->type == JSON_ARRAY
1066 [ + - ]: 10 : && params->u.array.n == 2
1067 [ + - ]: 10 : && params->u.array.elems[0]->type == JSON_NULL) {
1068 : 10 : monitor2_print(params->u.array.elems[1], mts, n_mts);
1069 : 10 : fflush(stdout);
1070 : : }
1071 : : }
1072 : 55 : jsonrpc_msg_destroy(msg);
1073 : : }
1074 : :
1075 [ + + ]: 96 : if (exiting) {
1076 : 1 : break;
1077 : : }
1078 : :
1079 : 95 : jsonrpc_run(rpc);
1080 : 95 : jsonrpc_wait(rpc);
1081 [ + + ]: 95 : if (!blocked) {
1082 : 92 : jsonrpc_recv_wait(rpc);
1083 : : }
1084 : 95 : unixctl_server_wait(unixctl);
1085 : 95 : poll_block();
1086 : 95 : }
1087 : :
1088 : 1 : json_destroy(request_id);
1089 : 1 : unixctl_server_destroy(unixctl);
1090 : 1 : ovsdb_schema_destroy(schema);
1091 : 1 : destroy_monitored_table(mts, n_mts);
1092 : 1 : }
1093 : :
1094 : : static void
1095 : 16 : do_monitor(struct jsonrpc *rpc, const char *database,
1096 : : int argc, char *argv[])
1097 : : {
1098 : 16 : do_monitor__(rpc, database, OVSDB_MONITOR_V1, argc, argv, NULL);
1099 : 1 : }
1100 : :
1101 : : static void
1102 : 7 : do_monitor_cond(struct jsonrpc *rpc, const char *database,
1103 : : int argc, char *argv[])
1104 : : {
1105 : : struct ovsdb_condition cnd;
1106 : 7 : struct json *condition = NULL;
1107 : : struct ovsdb_schema *schema;
1108 : : struct ovsdb_table_schema *table;
1109 : 7 : const char *table_name = argv[1];
1110 : :
1111 [ - + ]: 7 : ovs_assert(argc > 1);
1112 : 7 : schema = fetch_schema(rpc, database);
1113 : 7 : table = shash_find_data(&schema->tables, table_name);
1114 [ - + ]: 7 : if (!table) {
1115 : 0 : ovs_fatal(0, "%s does not have a table named \"%s\"",
1116 : : database, table_name);
1117 : : }
1118 : 7 : condition = parse_json(argv[0]);
1119 : 7 : check_ovsdb_error(ovsdb_condition_from_json(table, condition,
1120 : : NULL, &cnd));
1121 : 7 : ovsdb_condition_destroy(&cnd);
1122 : 7 : do_monitor__(rpc, database, OVSDB_MONITOR_V2, --argc, ++argv, condition);
1123 : 0 : ovsdb_schema_destroy(schema);
1124 : 0 : }
1125 : :
1126 : : struct dump_table_aux {
1127 : : struct ovsdb_datum **data;
1128 : : const struct ovsdb_column **columns;
1129 : : size_t n_columns;
1130 : : };
1131 : :
1132 : : static int
1133 : 198 : compare_data(size_t a_y, size_t b_y, size_t x,
1134 : : const struct dump_table_aux *aux)
1135 : : {
1136 : 198 : return ovsdb_datum_compare_3way(&aux->data[a_y][x],
1137 : 198 : &aux->data[b_y][x],
1138 : 198 : &aux->columns[x]->type);
1139 : : }
1140 : :
1141 : : static int
1142 : 180 : compare_rows(size_t a_y, size_t b_y, void *aux_)
1143 : : {
1144 : 180 : struct dump_table_aux *aux = aux_;
1145 : : size_t x;
1146 : :
1147 : : /* Skip UUID columns on the first pass, since their values tend to be
1148 : : * random and make our results less reproducible. */
1149 [ + - ]: 378 : for (x = 0; x < aux->n_columns; x++) {
1150 [ + + ]: 378 : if (aux->columns[x]->type.key.type != OVSDB_TYPE_UUID) {
1151 : 198 : int cmp = compare_data(a_y, b_y, x, aux);
1152 [ + + ]: 198 : if (cmp) {
1153 : 180 : return cmp;
1154 : : }
1155 : : }
1156 : : }
1157 : :
1158 : : /* Use UUID columns as tie-breakers. */
1159 [ # # ]: 0 : for (x = 0; x < aux->n_columns; x++) {
1160 [ # # ]: 0 : if (aux->columns[x]->type.key.type == OVSDB_TYPE_UUID) {
1161 : 0 : int cmp = compare_data(a_y, b_y, x, aux);
1162 [ # # ]: 0 : if (cmp) {
1163 : 0 : return cmp;
1164 : : }
1165 : : }
1166 : : }
1167 : :
1168 : 0 : return 0;
1169 : : }
1170 : :
1171 : : static void
1172 : 262 : swap_rows(size_t a_y, size_t b_y, void *aux_)
1173 : : {
1174 : 262 : struct dump_table_aux *aux = aux_;
1175 : 262 : struct ovsdb_datum *tmp = aux->data[a_y];
1176 : 262 : aux->data[a_y] = aux->data[b_y];
1177 : 262 : aux->data[b_y] = tmp;
1178 : 262 : }
1179 : :
1180 : : static int
1181 : 473 : compare_columns(const void *a_, const void *b_)
1182 : : {
1183 : 473 : const struct ovsdb_column *const *ap = a_;
1184 : 473 : const struct ovsdb_column *const *bp = b_;
1185 : 473 : const struct ovsdb_column *a = *ap;
1186 : 473 : const struct ovsdb_column *b = *bp;
1187 : :
1188 : 473 : return strcmp(a->name, b->name);
1189 : : }
1190 : :
1191 : : static void
1192 : 145 : dump_table(const char *table_name, const struct shash *cols,
1193 : : struct json_array *rows)
1194 : : {
1195 : : const struct ovsdb_column **columns;
1196 : : size_t n_columns;
1197 : :
1198 : : struct ovsdb_datum **data;
1199 : :
1200 : : struct dump_table_aux aux;
1201 : : struct shash_node *node;
1202 : : struct table t;
1203 : : size_t x, y;
1204 : :
1205 : : /* Sort columns by name, for reproducibility. */
1206 : 145 : columns = xmalloc(shash_count(cols) * sizeof *columns);
1207 : 145 : n_columns = 0;
1208 [ + + ][ - + ]: 743 : SHASH_FOR_EACH (node, cols) {
1209 : 598 : struct ovsdb_column *column = node->data;
1210 [ + + ]: 598 : if (strcmp(column->name, "_version")) {
1211 : 453 : columns[n_columns++] = column;
1212 : : }
1213 : : }
1214 : 145 : qsort(columns, n_columns, sizeof *columns, compare_columns);
1215 : :
1216 : : /* Extract data from table. */
1217 : 145 : data = xmalloc(rows->n * sizeof *data);
1218 [ + + ]: 306 : for (y = 0; y < rows->n; y++) {
1219 : : struct shash *row;
1220 : :
1221 [ - + ]: 161 : if (rows->elems[y]->type != JSON_OBJECT) {
1222 : 0 : ovs_fatal(0, "row %"PRIuSIZE" in table %s response is not a JSON object: "
1223 : 0 : "%s", y, table_name, json_to_string(rows->elems[y], 0));
1224 : : }
1225 : 161 : row = json_object(rows->elems[y]);
1226 : :
1227 : 161 : data[y] = xmalloc(n_columns * sizeof **data);
1228 [ + + ]: 628 : for (x = 0; x < n_columns; x++) {
1229 : 467 : const struct json *json = shash_find_data(row, columns[x]->name);
1230 [ - + ]: 467 : if (!json) {
1231 : 0 : ovs_fatal(0, "row %"PRIuSIZE" in table %s response lacks %s column",
1232 : 0 : y, table_name, columns[x]->name);
1233 : : }
1234 : :
1235 : 467 : check_ovsdb_error(ovsdb_datum_from_json(&data[y][x],
1236 : 467 : &columns[x]->type,
1237 : : json, NULL));
1238 : : }
1239 : : }
1240 : :
1241 : : /* Sort rows by column values, for reproducibility. */
1242 : 145 : aux.data = data;
1243 : 145 : aux.columns = columns;
1244 : 145 : aux.n_columns = n_columns;
1245 : 145 : sort(rows->n, compare_rows, swap_rows, &aux);
1246 : :
1247 : : /* Add column headings. */
1248 : 145 : table_init(&t);
1249 : 145 : table_set_caption(&t, xasprintf("%s table", table_name));
1250 [ + + ]: 598 : for (x = 0; x < n_columns; x++) {
1251 : 453 : table_add_column(&t, "%s", columns[x]->name);
1252 : : }
1253 : :
1254 : : /* Print rows. */
1255 [ + + ]: 306 : for (y = 0; y < rows->n; y++) {
1256 : 161 : table_add_row(&t);
1257 [ + + ]: 628 : for (x = 0; x < n_columns; x++) {
1258 : 467 : struct cell *cell = table_add_cell(&t);
1259 : 467 : cell->json = ovsdb_datum_to_json(&data[y][x], &columns[x]->type);
1260 : 467 : cell->type = &columns[x]->type;
1261 : 467 : ovsdb_datum_destroy(&data[y][x], &columns[x]->type);
1262 : : }
1263 : 161 : free(data[y]);
1264 : : }
1265 : 145 : table_print(&t, &table_style);
1266 : 145 : table_destroy(&t);
1267 : :
1268 : 145 : free(data);
1269 : 145 : free(columns);
1270 : 145 : }
1271 : :
1272 : : static void
1273 : 93 : do_dump(struct jsonrpc *rpc, const char *database,
1274 : : int argc, char *argv[])
1275 : : {
1276 : : struct jsonrpc_msg *request, *reply;
1277 : : struct ovsdb_schema *schema;
1278 : : struct json *transaction;
1279 : :
1280 : : const struct shash_node *node, **tables;
1281 : : size_t n_tables;
1282 : : struct ovsdb_table_schema *tschema;
1283 : : const struct shash *columns;
1284 : : struct shash custom_columns;
1285 : :
1286 : : size_t i;
1287 : :
1288 : 93 : shash_init(&custom_columns);
1289 : 93 : schema = fetch_schema(rpc, database);
1290 [ - + ]: 93 : if (argc) {
1291 : 0 : node = shash_find(&schema->tables, argv[0]);
1292 [ # # ]: 0 : if (!node) {
1293 : 0 : ovs_fatal(0, "No table \"%s\" found.", argv[0]);
1294 : : }
1295 : 0 : tables = xmemdup(&node, sizeof(&node));
1296 : 0 : n_tables = 1;
1297 : 0 : tschema = tables[0]->data;
1298 [ # # ]: 0 : for (i = 1; i < argc; i++) {
1299 : 0 : node = shash_find(&tschema->columns, argv[i]);
1300 [ # # ]: 0 : if (!node) {
1301 : 0 : ovs_fatal(0, "Table \"%s\" has no column %s.", argv[0], argv[1]);
1302 : : }
1303 : 0 : shash_add(&custom_columns, argv[1], node->data);
1304 : : }
1305 : : } else {
1306 : 93 : tables = shash_sort(&schema->tables);
1307 : 93 : n_tables = shash_count(&schema->tables);
1308 : : }
1309 : :
1310 : : /* Construct transaction to retrieve entire database. */
1311 : 93 : transaction = json_array_create_1(json_string_create(database));
1312 [ + + ]: 238 : for (i = 0; i < n_tables; i++) {
1313 : 145 : const struct ovsdb_table_schema *ts = tables[i]->data;
1314 : : struct json *op, *jcolumns;
1315 : :
1316 [ - + ]: 145 : if (argc > 1) {
1317 : 0 : columns = &custom_columns;
1318 : : } else {
1319 : 145 : columns = &ts->columns;
1320 : : }
1321 : 145 : jcolumns = json_array_create_empty();
1322 [ + + ][ - + ]: 743 : SHASH_FOR_EACH (node, columns) {
1323 : 598 : const struct ovsdb_column *column = node->data;
1324 : :
1325 [ + + ]: 598 : if (strcmp(column->name, "_version")) {
1326 : 453 : json_array_add(jcolumns, json_string_create(column->name));
1327 : : }
1328 : : }
1329 : :
1330 : 145 : op = json_object_create();
1331 : 145 : json_object_put_string(op, "op", "select");
1332 : 145 : json_object_put_string(op, "table", tables[i]->name);
1333 : 145 : json_object_put(op, "where", json_array_create_empty());
1334 : 145 : json_object_put(op, "columns", jcolumns);
1335 : 145 : json_array_add(transaction, op);
1336 : : }
1337 : :
1338 : : /* Send request, get reply. */
1339 : 93 : request = jsonrpc_create_request("transact", transaction, NULL);
1340 : 93 : check_txn(jsonrpc_transact_block(rpc, request, &reply), &reply);
1341 : :
1342 : : /* Print database contents. */
1343 [ + - ]: 93 : if (reply->result->type != JSON_ARRAY
1344 [ - + ]: 93 : || reply->result->u.array.n != n_tables) {
1345 : 0 : ovs_fatal(0, "reply is not array of %"PRIuSIZE" elements: %s",
1346 : 0 : n_tables, json_to_string(reply->result, 0));
1347 : : }
1348 [ + + ]: 238 : for (i = 0; i < n_tables; i++) {
1349 : 145 : const struct ovsdb_table_schema *ts = tables[i]->data;
1350 : 145 : const struct json *op_result = reply->result->u.array.elems[i];
1351 : : struct json *rows;
1352 : :
1353 [ + - ]: 145 : if (op_result->type != JSON_OBJECT
1354 [ + - ]: 145 : || !(rows = shash_find_data(json_object(op_result), "rows"))
1355 [ - + ]: 145 : || rows->type != JSON_ARRAY) {
1356 : 0 : ovs_fatal(0, "%s table reply is not an object with a \"rows\" "
1357 : : "member array: %s",
1358 : : ts->name, json_to_string(op_result, 0));
1359 : : }
1360 : :
1361 [ - + ]: 145 : if (argc > 1) {
1362 : 0 : dump_table(tables[i]->name, &custom_columns, &rows->u.array);
1363 : : } else {
1364 : 145 : dump_table(tables[i]->name, &ts->columns, &rows->u.array);
1365 : : }
1366 : : }
1367 : :
1368 : 93 : jsonrpc_msg_destroy(reply);
1369 : 93 : shash_destroy(&custom_columns);
1370 : 93 : free(tables);
1371 : 93 : ovsdb_schema_destroy(schema);
1372 : 93 : }
1373 : :
1374 : : static void
1375 : 0 : do_help(struct jsonrpc *rpc OVS_UNUSED, const char *database OVS_UNUSED,
1376 : : int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
1377 : : {
1378 : 0 : usage();
1379 : : }
1380 : :
1381 : :
1382 : : /* "lock" command. */
1383 : :
1384 : : struct ovsdb_client_lock_req {
1385 : : const char *method;
1386 : : char *lock;
1387 : : };
1388 : :
1389 : : static void
1390 : 8 : lock_req_init(struct ovsdb_client_lock_req *lock_req,
1391 : : const char *method, const char *lock_name)
1392 : : {
1393 [ + - ][ - + ]: 8 : if (lock_req->method || lock_req->lock) {
1394 : 0 : return;
1395 : : }
1396 : 8 : lock_req->method = method;
1397 : 8 : lock_req->lock = xstrdup(lock_name);
1398 : : }
1399 : :
1400 : : static bool
1401 : 35 : lock_req_is_set(struct ovsdb_client_lock_req *lock_req)
1402 : : {
1403 : 35 : return lock_req->method;
1404 : : }
1405 : :
1406 : : static void
1407 : 8 : lock_req_destroy(struct ovsdb_client_lock_req *lock_req)
1408 : : {
1409 : 8 : free(lock_req->lock);
1410 : 8 : lock_req->method = NULL;
1411 : 8 : lock_req->lock = NULL;
1412 : 8 : }
1413 : :
1414 : : /* Create a lock class request. Caller is responsible for free
1415 : : * the 'request' message. */
1416 : : static struct jsonrpc_msg *
1417 : 8 : create_lock_request(struct ovsdb_client_lock_req *lock_req)
1418 : : {
1419 : : struct json *locks, *lock;
1420 : :
1421 : 8 : locks = json_array_create_empty();
1422 : 8 : lock = json_string_create(lock_req->lock);
1423 : 8 : json_array_add(locks, lock);
1424 : :
1425 : 8 : return jsonrpc_create_request(lock_req->method, locks, NULL);
1426 : : }
1427 : :
1428 : : static void
1429 : 0 : ovsdb_client_lock(struct unixctl_conn *conn, int argc OVS_UNUSED,
1430 : : const char *argv[], void *lock_req_)
1431 : : {
1432 : 0 : struct ovsdb_client_lock_req *lock_req = lock_req_;
1433 : 0 : lock_req_init(lock_req, "lock", argv[1]);
1434 : 0 : unixctl_command_reply(conn, NULL);
1435 : 0 : }
1436 : :
1437 : : static void
1438 : 2 : ovsdb_client_unlock(struct unixctl_conn *conn, int argc OVS_UNUSED,
1439 : : const char *argv[], void *lock_req_)
1440 : : {
1441 : 2 : struct ovsdb_client_lock_req *lock_req = lock_req_;
1442 : 2 : lock_req_init(lock_req, "unlock", argv[1]);
1443 : 2 : unixctl_command_reply(conn, NULL);
1444 : 2 : }
1445 : :
1446 : : static void
1447 : 0 : ovsdb_client_steal(struct unixctl_conn *conn, int argc OVS_UNUSED,
1448 : : const char *argv[], void *lock_req_)
1449 : : {
1450 : 0 : struct ovsdb_client_lock_req *lock_req = lock_req_;
1451 : 0 : lock_req_init(lock_req, "steal", argv[1]);
1452 : 0 : unixctl_command_reply(conn, NULL);
1453 : 0 : }
1454 : :
1455 : : static void
1456 : 6 : do_lock(struct jsonrpc *rpc, const char *method, const char *lock)
1457 : : {
1458 : 6 : struct ovsdb_client_lock_req lock_req = {NULL, NULL};
1459 : : struct unixctl_server *unixctl;
1460 : : struct jsonrpc_msg *request;
1461 : 6 : struct json *request_id = NULL;
1462 : 6 : bool exiting = false;
1463 : 6 : bool enable_lock_request = true; /* Don't send another request before
1464 : : getting a reply of the previous
1465 : : request. */
1466 : 6 : daemon_save_fd(STDOUT_FILENO);
1467 : 6 : daemonize_start(false);
1468 : 6 : lock_req_init(&lock_req, method, lock);
1469 : :
1470 [ + - ]: 6 : if (get_detach()) {
1471 : : int error;
1472 : :
1473 : 6 : error = unixctl_server_create(NULL, &unixctl);
1474 [ - + ]: 6 : if (error) {
1475 : 0 : ovs_fatal(error, "failed to create unixctl server");
1476 : : }
1477 : :
1478 : 6 : unixctl_command_register("unlock", "LOCK", 1, 1,
1479 : : ovsdb_client_unlock, &lock_req);
1480 : 6 : unixctl_command_register("steal", "LOCK", 1, 1,
1481 : : ovsdb_client_steal, &lock_req);
1482 : 6 : unixctl_command_register("lock", "LOCK", 1, 1,
1483 : : ovsdb_client_lock, &lock_req);
1484 : 6 : unixctl_command_register("exit", "", 0, 0,
1485 : : ovsdb_client_exit, &exiting);
1486 : : } else {
1487 : 0 : unixctl = NULL;
1488 : : }
1489 : :
1490 : : for (;;) {
1491 : : struct jsonrpc_msg *msg;
1492 : : int error;
1493 : :
1494 : 35 : unixctl_server_run(unixctl);
1495 [ + - ][ + + ]: 35 : if (enable_lock_request && lock_req_is_set(&lock_req)) {
1496 : 8 : request = create_lock_request(&lock_req);
1497 : 8 : request_id = json_clone(request->id);
1498 : 8 : jsonrpc_send(rpc, request);
1499 : 8 : lock_req_destroy(&lock_req);
1500 : : }
1501 : :
1502 : 35 : error = jsonrpc_recv(rpc, &msg);
1503 [ + + ]: 35 : if (error == EAGAIN) {
1504 : 18 : goto no_msg;
1505 [ + + ]: 17 : } else if (error) {
1506 : 6 : ovs_fatal(error, "%s: receive failed", jsonrpc_get_name(rpc));
1507 : : }
1508 : :
1509 [ - + ][ # # ]: 11 : if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
1510 : 0 : jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params),
1511 : 0 : msg->id));
1512 [ + + ]: 11 : } else if (msg->type == JSONRPC_REPLY
1513 [ + - ]: 8 : && json_equal(msg->id, request_id)) {
1514 : 8 : print_json(msg->result);
1515 : 8 : putchar('\n');
1516 : 8 : fflush(stdout);
1517 : 8 : enable_lock_request = true;
1518 : 8 : json_destroy(request_id);
1519 : 8 : request_id = NULL;
1520 : 8 : daemonize_complete();
1521 [ + - ]: 3 : } else if (msg->type == JSONRPC_NOTIFY) {
1522 : 3 : puts(msg->method);
1523 : 3 : print_json(msg->params);
1524 : 3 : putchar('\n');
1525 : 3 : fflush(stdout);
1526 : : }
1527 : :
1528 : 11 : jsonrpc_msg_destroy(msg);
1529 : :
1530 : : no_msg:
1531 [ - + ]: 29 : if (exiting) {
1532 : 0 : break;
1533 : : }
1534 : :
1535 : 29 : jsonrpc_run(rpc);
1536 : 29 : jsonrpc_wait(rpc);
1537 : 29 : jsonrpc_recv_wait(rpc);
1538 : :
1539 : 29 : unixctl_server_wait(unixctl);
1540 : 29 : poll_block();
1541 : 29 : }
1542 : :
1543 : 0 : json_destroy(request_id);
1544 : 0 : unixctl_server_destroy(unixctl);
1545 : 0 : }
1546 : :
1547 : : static void
1548 : 5 : do_lock_create(struct jsonrpc *rpc, const char *database OVS_UNUSED,
1549 : : int argc OVS_UNUSED, char *argv[])
1550 : : {
1551 : 5 : do_lock(rpc, "lock", argv[0]);
1552 : 0 : }
1553 : :
1554 : : static void
1555 : 1 : do_lock_steal(struct jsonrpc *rpc, const char *database OVS_UNUSED,
1556 : : int argc OVS_UNUSED, char *argv[])
1557 : : {
1558 : 1 : do_lock(rpc, "steal", argv[0]);
1559 : 0 : }
1560 : :
1561 : : static void
1562 : 0 : do_lock_unlock(struct jsonrpc *rpc, const char *database OVS_UNUSED,
1563 : : int argc OVS_UNUSED, char *argv[])
1564 : : {
1565 : 0 : do_lock(rpc, "unlock", argv[0]);
1566 : 0 : }
1567 : :
1568 : : /* All command handlers (except for "help") are expected to take an optional
1569 : : * server socket name (e.g. "unix:...") as their first argument. The socket
1570 : : * name argument must be included in max_args (but left out of min_args). The
1571 : : * command name and socket name are not included in the arguments passed to the
1572 : : * handler: the argv[0] passed to the handler is the first argument after the
1573 : : * optional server socket name. The connection to the server is available as
1574 : : * global variable 'rpc'. */
1575 : : static const struct ovsdb_client_command all_commands[] = {
1576 : : { "list-dbs", NEED_RPC, 0, 0, do_list_dbs },
1577 : : { "get-schema", NEED_DATABASE, 0, 0, do_get_schema },
1578 : : { "get-schema-version", NEED_DATABASE, 0, 0, do_get_schema_version },
1579 : : { "list-tables", NEED_DATABASE, 0, 0, do_list_tables },
1580 : : { "list-columns", NEED_DATABASE, 0, 1, do_list_columns },
1581 : : { "transact", NEED_RPC, 1, 1, do_transact },
1582 : : { "monitor", NEED_DATABASE, 1, INT_MAX, do_monitor },
1583 : : { "monitor-cond", NEED_DATABASE, 2, 3, do_monitor_cond },
1584 : : { "dump", NEED_DATABASE, 0, INT_MAX, do_dump },
1585 : : { "lock", NEED_RPC, 1, 1, do_lock_create },
1586 : : { "steal", NEED_RPC, 1, 1, do_lock_steal },
1587 : : { "unlock", NEED_RPC, 1, 1, do_lock_unlock },
1588 : : { "help", NEED_NONE, 0, INT_MAX, do_help },
1589 : :
1590 : : { NULL, 0, 0, 0, NULL },
1591 : : };
1592 : :
1593 : 906 : static const struct ovsdb_client_command *get_all_commands(void)
1594 : : {
1595 : 906 : return all_commands;
1596 : : }
|