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