ovsdb-client: Improve HTML output.
[cascardo/ovs.git] / ovsdb / ovsdb-client.c
1 /*
2  * Copyright (c) 2009, 2010 Nicira Networks.
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 <assert.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 "dynamic-string.h"
33 #include "json.h"
34 #include "jsonrpc.h"
35 #include "ovsdb.h"
36 #include "ovsdb-data.h"
37 #include "ovsdb-error.h"
38 #include "stream.h"
39 #include "stream-ssl.h"
40 #include "table.h"
41 #include "timeval.h"
42 #include "util.h"
43
44 #include "vlog.h"
45 #define THIS_MODULE VLM_ovsdb_client
46
47 /* --format: Output formatting. */
48 static enum {
49     FMT_TABLE,                  /* Textual table. */
50     FMT_HTML,                   /* HTML table. */
51     FMT_CSV                     /* Comma-separated lines. */
52 } output_format;
53
54 /* --no-headings: Whether table output should include headings. */
55 static int output_headings = true;
56
57 /* --pretty: Flags to pass to json_to_string(). */
58 static int json_flags = JSSF_SORT;
59
60 /* --data: Format of data in output tables. */
61 static enum {
62     DF_STRING,                  /* String format. */
63     DF_JSON,                    /* JSON. */
64 } data_format;
65
66 static const struct command all_commands[];
67
68 static void usage(void) NO_RETURN;
69 static void parse_options(int argc, char *argv[]);
70
71 int
72 main(int argc, char *argv[])
73 {
74     proctitle_init(argc, argv);
75     set_program_name(argv[0]);
76     time_init();
77     vlog_init();
78     parse_options(argc, argv);
79     signal(SIGPIPE, SIG_IGN);
80     run_command(argc - optind, argv + optind, all_commands);
81     return 0;
82 }
83
84 static void
85 parse_options(int argc, char *argv[])
86 {
87     enum {
88         OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1
89     };
90     static struct option long_options[] = {
91         {"format", required_argument, 0, 'f'},
92         {"data", required_argument, 0, 'd'},
93         {"no-headings", no_argument, &output_headings, 0},
94         {"pretty", no_argument, &json_flags, JSSF_PRETTY | JSSF_SORT},
95         {"verbose", optional_argument, 0, 'v'},
96         {"help", no_argument, 0, 'h'},
97         {"version", no_argument, 0, 'V'},
98         DAEMON_LONG_OPTIONS,
99 #ifdef HAVE_OPENSSL
100         {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT},
101         STREAM_SSL_LONG_OPTIONS
102 #endif
103         {0, 0, 0, 0},
104     };
105     char *short_options = long_options_to_short_options(long_options);
106
107     for (;;) {
108         int c;
109
110         c = getopt_long(argc, argv, short_options, long_options, NULL);
111         if (c == -1) {
112             break;
113         }
114
115         switch (c) {
116         case 'f':
117             if (!strcmp(optarg, "table")) {
118                 output_format = FMT_TABLE;
119             } else if (!strcmp(optarg, "html")) {
120                 output_format = FMT_HTML;
121             } else if (!strcmp(optarg, "csv")) {
122                 output_format = FMT_CSV;
123             } else {
124                 ovs_fatal(0, "unknown output format \"%s\"", optarg);
125             }
126             break;
127
128         case 'd':
129             if (!strcmp(optarg, "string")) {
130                 data_format = DF_STRING;
131             } else if (!strcmp(optarg, "json")) {
132                 data_format = DF_JSON;
133             } else {
134                 ovs_fatal(0, "unknown data format \"%s\"", optarg);
135             }
136             break;
137
138         case 'h':
139             usage();
140
141         case 'V':
142             OVS_PRINT_VERSION(0, 0);
143             exit(EXIT_SUCCESS);
144
145         case 'v':
146             vlog_set_verbosity(optarg);
147             break;
148
149         DAEMON_OPTION_HANDLERS
150
151 #ifdef HAVE_OPENSSL
152         STREAM_SSL_OPTION_HANDLERS
153
154         case OPT_BOOTSTRAP_CA_CERT:
155             stream_ssl_set_ca_cert_file(optarg, true);
156             break;
157 #endif
158
159         case '?':
160             exit(EXIT_FAILURE);
161
162         case 0:
163             /* getopt_long() already set the value for us. */
164             break;
165
166         default:
167             abort();
168         }
169     }
170     free(short_options);
171 }
172
173 static void
174 usage(void)
175 {
176     printf("%s: Open vSwitch database JSON-RPC client\n"
177            "usage: %s [OPTIONS] COMMAND [ARG...]\n"
178            "\nValid commands are:\n"
179            "\n  list-dbs SERVER\n"
180            "    list databases available on SERVER\n"
181            "\n  get-schema SERVER DATABASE\n"
182            "    retrieve schema for DATABASE from SERVER\n"
183            "\n  list-tables SERVER DATABSE\n"
184            "    list tables for DATABSAE on SERVER\n"
185            "\n  list-columns SERVER DATABASE [TABLE]\n"
186            "    list columns in TABLE (or all tables) in DATABASE on SERVER\n"
187            "\n  transact SERVER TRANSACTION\n"
188            "    run TRANSACTION (a JSON array of operations) on SERVER\n"
189            "    and print the results as JSON on stdout\n"
190            "\n  monitor SERVER DATABASE TABLE [COLUMN,...] [SELECT,...]\n"
191            "    monitor contents of (COLUMNs in) TABLE in DATABASE on SERVER\n"
192            "    Valid SELECTs are: initial, insert, delete, modify\n",
193            program_name, program_name);
194     stream_usage("SERVER", true, true, true);
195     printf("\nOutput formatting options:\n"
196            "  -f, --format=FORMAT         set output formatting to FORMAT\n"
197            "                              (\"table\", \"html\", or \"csv\"\n"
198            "  --no-headings               omit table heading row\n"
199            "  --pretty                    pretty-print JSON in output");
200     daemon_usage();
201     vlog_usage();
202     printf("\nOther options:\n"
203            "  -h, --help                  display this help message\n"
204            "  -V, --version               display version information\n");
205     exit(EXIT_SUCCESS);
206 }
207 \f
208 static struct json *
209 parse_json(const char *s)
210 {
211     struct json *json = json_from_string(s);
212     if (json->type == JSON_STRING) {
213         ovs_fatal(0, "\"%s\": %s", s, json->u.string);
214     }
215     return json;
216 }
217
218 static struct jsonrpc *
219 open_jsonrpc(const char *server)
220 {
221     struct stream *stream;
222     int error;
223
224     error = stream_open_block(server, &stream);
225     if (error == EAFNOSUPPORT) {
226         struct pstream *pstream;
227
228         error = pstream_open(server, &pstream);
229         if (error) {
230             ovs_fatal(error, "failed to connect or listen to \"%s\"", server);
231         }
232
233         VLOG_INFO("%s: waiting for connection...", server);
234         error = pstream_accept_block(pstream, &stream);
235         if (error) {
236             ovs_fatal(error, "failed to accept connection on \"%s\"", server);
237         }
238
239         pstream_close(pstream);
240     } else if (error) {
241         ovs_fatal(error, "failed to connect to \"%s\"", server);
242     }
243
244     return jsonrpc_open(stream);
245 }
246
247 static void
248 print_json(struct json *json)
249 {
250     char *string = json_to_string(json, json_flags);
251     fputs(string, stdout);
252     free(string);
253 }
254
255 static void
256 print_and_free_json(struct json *json)
257 {
258     print_json(json);
259     json_destroy(json);
260 }
261
262 static void
263 check_ovsdb_error(struct ovsdb_error *error)
264 {
265     if (error) {
266         ovs_fatal(0, "%s", ovsdb_error_to_string(error));
267     }
268 }
269
270 static struct ovsdb_schema *
271 fetch_schema_from_rpc(struct jsonrpc *rpc, const char *database)
272 {
273     struct jsonrpc_msg *request, *reply;
274     struct ovsdb_schema *schema;
275     int error;
276
277     request = jsonrpc_create_request("get_schema",
278                                      json_array_create_1(
279                                          json_string_create(database)),
280                                      NULL);
281     error = jsonrpc_transact_block(rpc, request, &reply);
282     if (error) {
283         ovs_fatal(error, "transaction failed");
284     }
285     check_ovsdb_error(ovsdb_schema_from_json(reply->result, &schema));
286     jsonrpc_msg_destroy(reply);
287
288     return schema;
289 }
290
291 static struct ovsdb_schema *
292 fetch_schema(const char *server, const char *database)
293 {
294     struct ovsdb_schema *schema;
295     struct jsonrpc *rpc;
296
297     rpc = open_jsonrpc(server);
298     schema = fetch_schema_from_rpc(rpc, database);
299     jsonrpc_close(rpc);
300
301     return schema;
302 }
303 \f
304 struct column {
305     char *heading;
306     int width;
307 };
308
309 struct table {
310     char **cells;
311     struct column *columns;
312     size_t n_columns, allocated_columns;
313     size_t n_rows, allocated_rows;
314     size_t current_column;
315 };
316
317 static void
318 table_init(struct table *table)
319 {
320     memset(table, 0, sizeof *table);
321 }
322
323 static void
324 table_destroy(struct table *table)
325 {
326     size_t i;
327
328     for (i = 0; i < table->n_columns; i++) {
329         free(table->columns[i].heading);
330     }
331     free(table->columns);
332
333     for (i = 0; i < table->n_columns * table->n_rows; i++) {
334         free(table->cells[i]);
335     }
336     free(table->cells);
337 }
338
339 static void
340 table_add_column(struct table *table, const char *heading, ...)
341     PRINTF_FORMAT(2, 3);
342
343 static void
344 table_add_column(struct table *table, const char *heading, ...)
345 {
346     struct column *column;
347     va_list args;
348
349     assert(!table->n_rows);
350     if (table->n_columns >= table->allocated_columns) {
351         table->columns = x2nrealloc(table->columns, &table->allocated_columns,
352                                     sizeof *table->columns);
353     }
354     column = &table->columns[table->n_columns++];
355
356     va_start(args, heading);
357     column->heading = xvasprintf(heading, args);
358     column->width = strlen(column->heading);
359     va_end(args);
360 }
361
362 static char **
363 table_cell__(const struct table *table, size_t row, size_t column)
364 {
365     return &table->cells[column + row * table->n_columns];
366 }
367
368 static void
369 table_add_row(struct table *table)
370 {
371     size_t x, y;
372
373     if (table->n_rows >= table->allocated_rows) {
374         table->cells = x2nrealloc(table->cells, &table->allocated_rows,
375                                   table->n_columns * sizeof *table->cells);
376     }
377
378     y = table->n_rows++;
379     table->current_column = 0;
380     for (x = 0; x < table->n_columns; x++) {
381         *table_cell__(table, y, x) = NULL;
382     }
383 }
384
385 static void
386 table_add_cell_nocopy(struct table *table, char *s)
387 {
388     size_t x, y;
389     int length;
390
391     assert(table->n_rows > 0);
392     assert(table->current_column < table->n_columns);
393
394     x = table->current_column++;
395     y = table->n_rows - 1;
396     *table_cell__(table, y, x) = s;
397
398     length = strlen(s);
399     if (length > table->columns[x].width) {
400         table->columns[x].width = length;
401     }
402 }
403
404 static void
405 table_add_cell(struct table *table, const char *format, ...)
406 {
407     va_list args;
408
409     va_start(args, format);
410     table_add_cell_nocopy(table, xvasprintf(format, args));
411     va_end(args);
412 }
413
414 static void
415 table_print_table_line__(struct ds *line)
416 {
417     puts(ds_cstr(line));
418     ds_clear(line);
419 }
420
421 static void
422 table_print_table__(const struct table *table)
423 {
424     struct ds line = DS_EMPTY_INITIALIZER;
425     size_t x, y;
426
427     if (output_headings) {
428         for (x = 0; x < table->n_columns; x++) {
429             const struct column *column = &table->columns[x];
430             if (x) {
431                 ds_put_char(&line, ' ');
432             }
433             ds_put_format(&line, "%-*s", column->width, column->heading);
434         }
435         table_print_table_line__(&line);
436
437         for (x = 0; x < table->n_columns; x++) {
438             const struct column *column = &table->columns[x];
439             int i;
440
441             if (x) {
442                 ds_put_char(&line, ' ');
443             }
444             for (i = 0; i < column->width; i++) {
445                 ds_put_char(&line, '-');
446             }
447         }
448         table_print_table_line__(&line);
449     }
450
451     for (y = 0; y < table->n_rows; y++) {
452         for (x = 0; x < table->n_columns; x++) {
453             const char *cell = *table_cell__(table, y, x);
454             if (x) {
455                 ds_put_char(&line, ' ');
456             }
457             ds_put_format(&line, "%-*s", table->columns[x].width, cell);
458         }
459         table_print_table_line__(&line);
460     }
461
462     ds_destroy(&line);
463 }
464
465 static void
466 table_escape_html_text__(const char *s, size_t n)
467 {
468     size_t i;
469
470     for (i = 0; i < n; i++) {
471         char c = s[i];
472
473         switch (c) {
474         case '&':
475             fputs("&amp;", stdout);
476             break;
477         case '<':
478             fputs("&lt;", stdout);
479             break;
480         case '>':
481             fputs("&gt;", stdout);
482             break;
483         case '"':
484             fputs("&quot;", stdout);
485             break;
486         default:
487             putchar(c);
488             break;
489         }
490     }
491 }
492
493 static void
494 table_print_html_cell__(const char *element, const char *content)
495 {
496     const char *p;
497
498     printf("    <%s>", element);
499     for (p = content; *p; ) {
500         struct uuid uuid;
501
502         if (uuid_from_string_prefix(&uuid, p)) {
503             printf("<a href=\"#%.*s\">%.*s</a>", UUID_LEN, p, 8, p);
504             p += UUID_LEN;
505         } else {
506             table_escape_html_text__(p, 1);
507             p++;
508         }
509     }
510     printf("</%s>\n", element);
511 }
512
513 static void
514 table_print_html__(const struct table *table)
515 {
516     size_t x, y;
517
518     fputs("<table border=1>\n", stdout);
519
520     if (output_headings) {
521         fputs("  <tr>\n", stdout);
522         for (x = 0; x < table->n_columns; x++) {
523             const struct column *column = &table->columns[x];
524             table_print_html_cell__("th", column->heading);
525         }
526         fputs("  </tr>\n", stdout);
527     }
528
529     for (y = 0; y < table->n_rows; y++) {
530         fputs("  <tr>\n", stdout);
531         for (x = 0; x < table->n_columns; x++) {
532             const char *content = *table_cell__(table, y, x);
533
534             if (!strcmp(table->columns[x].heading, "_uuid")) {
535                 fputs("    <td><a name=\"", stdout);
536                 table_escape_html_text__(content, strlen(content));
537                 fputs("\">", stdout);
538                 table_escape_html_text__(content, 8);
539                 fputs("</a></td>\n", stdout);
540             } else {
541                 table_print_html_cell__("td", content);
542             }
543         }
544         fputs("  </tr>\n", stdout);
545     }
546
547     fputs("</table>\n", stdout);
548 }
549
550 static void
551 table_print_csv_cell__(const char *content)
552 {
553     const char *p;
554
555     if (!strpbrk(content, "\n\",")) {
556         fputs(content, stdout);
557     } else {
558         putchar('"');
559         for (p = content; *p != '\0'; p++) {
560             switch (*p) {
561             case '"':
562                 fputs("\"\"", stdout);
563                 break;
564             default:
565                 putchar(*p);
566                 break;
567             }
568         }
569         putchar('"');
570     }
571 }
572
573 static void
574 table_print_csv__(const struct table *table)
575 {
576     size_t x, y;
577
578     if (output_headings) {
579         for (x = 0; x < table->n_columns; x++) {
580             const struct column *column = &table->columns[x];
581             if (x) {
582                 putchar(',');
583             }
584             table_print_csv_cell__(column->heading);
585         }
586         putchar('\n');
587     }
588
589     for (y = 0; y < table->n_rows; y++) {
590         for (x = 0; x < table->n_columns; x++) {
591             if (x) {
592                 putchar(',');
593             }
594             table_print_csv_cell__(*table_cell__(table, y, x));
595         }
596         putchar('\n');
597     }
598 }
599
600 static void
601 table_print(const struct table *table)
602 {
603     switch (output_format) {
604     case FMT_TABLE:
605         table_print_table__(table);
606         break;
607
608     case FMT_HTML:
609         table_print_html__(table);
610         break;
611
612     case FMT_CSV:
613         table_print_csv__(table);
614         break;
615     }
616 }
617 \f
618 static void
619 do_list_dbs(int argc OVS_UNUSED, char *argv[])
620 {
621     struct jsonrpc_msg *request, *reply;
622     struct jsonrpc *rpc;
623     int error;
624     size_t i;
625
626     rpc = open_jsonrpc(argv[1]);
627     request = jsonrpc_create_request("list_dbs", json_array_create_empty(),
628                                      NULL);
629     error = jsonrpc_transact_block(rpc, request, &reply);
630     if (error) {
631         ovs_fatal(error, "transaction failed");
632     }
633
634     if (reply->result->type != JSON_ARRAY) {
635         ovs_fatal(0, "list_dbs response is not array");
636     }
637
638     for (i = 0; i < reply->result->u.array.n; i++) {
639         const struct json *name = reply->result->u.array.elems[i];
640
641         if (name->type != JSON_STRING) {
642             ovs_fatal(0, "list_dbs response %zu is not string", i);
643         }
644         puts(name->u.string);
645     }
646     jsonrpc_msg_destroy(reply);
647 }
648
649 static void
650 do_get_schema(int argc OVS_UNUSED, char *argv[])
651 {
652     struct ovsdb_schema *schema = fetch_schema(argv[1], argv[2]);
653     print_and_free_json(ovsdb_schema_to_json(schema));
654     ovsdb_schema_destroy(schema);
655 }
656
657 static void
658 do_list_tables(int argc OVS_UNUSED, char *argv[])
659 {
660     struct ovsdb_schema *schema;
661     struct shash_node *node;
662     struct table t;
663
664     schema = fetch_schema(argv[1], argv[2]);
665     table_init(&t);
666     table_add_column(&t, "Table");
667     table_add_column(&t, "Comment");
668     SHASH_FOR_EACH (node, &schema->tables) {
669         struct ovsdb_table_schema *ts = node->data;
670
671         table_add_row(&t);
672         table_add_cell(&t, ts->name);
673         if (ts->comment) {
674             table_add_cell(&t, ts->comment);
675         }
676     }
677     ovsdb_schema_destroy(schema);
678     table_print(&t);
679 }
680
681 static void
682 do_list_columns(int argc OVS_UNUSED, char *argv[])
683 {
684     const char *table_name = argv[3];
685     struct ovsdb_schema *schema;
686     struct shash_node *table_node;
687     struct table t;
688
689     schema = fetch_schema(argv[1], argv[2]);
690     table_init(&t);
691     if (!table_name) {
692         table_add_column(&t, "Table");
693     }
694     table_add_column(&t, "Column");
695     table_add_column(&t, "Type");
696     table_add_column(&t, "Comment");
697     SHASH_FOR_EACH (table_node, &schema->tables) {
698         struct ovsdb_table_schema *ts = table_node->data;
699
700         if (!table_name || !strcmp(table_name, ts->name)) {
701             struct shash_node *column_node;
702
703             SHASH_FOR_EACH (column_node, &ts->columns) {
704                 const struct ovsdb_column *column = column_node->data;
705                 struct json *type = ovsdb_type_to_json(&column->type);
706
707                 table_add_row(&t);
708                 if (!table_name) {
709                     table_add_cell(&t, ts->name);
710                 }
711                 table_add_cell(&t, column->name);
712                 table_add_cell_nocopy(&t, json_to_string(type, JSSF_SORT));
713                 if (column->comment) {
714                     table_add_cell(&t, column->comment);
715                 }
716
717                 json_destroy(type);
718             }
719         }
720     }
721     ovsdb_schema_destroy(schema);
722     table_print(&t);
723 }
724
725 static void
726 do_transact(int argc OVS_UNUSED, char *argv[])
727 {
728     struct jsonrpc_msg *request, *reply;
729     struct json *transaction;
730     struct jsonrpc *rpc;
731     int error;
732
733     transaction = parse_json(argv[2]);
734
735     rpc = open_jsonrpc(argv[1]);
736     request = jsonrpc_create_request("transact", transaction, NULL);
737     error = jsonrpc_transact_block(rpc, request, &reply);
738     if (error) {
739         ovs_fatal(error, "transaction failed");
740     }
741     if (reply->error) {
742         ovs_fatal(error, "transaction returned error: %s",
743                   json_to_string(reply->error, json_flags));
744     }
745     print_json(reply->result);
746     putchar('\n');
747     jsonrpc_msg_destroy(reply);
748     jsonrpc_close(rpc);
749 }
750
751 static char *
752 format_data(const struct json *json, const struct ovsdb_type *type)
753 {
754     if (data_format == DF_JSON) {
755         return json_to_string(json, JSSF_SORT);
756     } else if (data_format == DF_STRING) {
757         struct ovsdb_datum datum;
758         struct ovsdb_error *error;
759         struct ds s;
760
761         error = ovsdb_datum_from_json(&datum, type, json, NULL);
762         if (error) {
763             return json_to_string(json, JSSF_SORT);
764         }
765
766         ds_init(&s);
767         ovsdb_datum_to_string(&datum, type, &s);
768         ovsdb_datum_destroy(&datum, type);
769         return ds_steal_cstr(&s);
770     } else {
771         NOT_REACHED();
772     }
773 }
774
775 static void
776 monitor_print_row(struct json *row, const char *type, const char *uuid,
777                   const struct ovsdb_column_set *columns, struct table *t)
778 {
779     size_t i;
780
781     if (!row) {
782         ovs_error(0, "missing %s row", type);
783         return;
784     } else if (row->type != JSON_OBJECT) {
785         ovs_error(0, "<row> is not object");
786         return;
787     }
788
789     table_add_row(t);
790     table_add_cell(t, uuid);
791     table_add_cell(t, type);
792     for (i = 0; i < columns->n_columns; i++) {
793         const struct ovsdb_column *column = columns->columns[i];
794         struct json *value = shash_find_data(json_object(row), column->name);
795         if (value) {
796             table_add_cell_nocopy(t, format_data(value, &column->type));
797         } else {
798             table_add_cell(t, "");
799         }
800     }
801 }
802
803 static void
804 monitor_print(struct json *table_updates,
805               const struct ovsdb_table_schema *table,
806               const struct ovsdb_column_set *columns, bool initial)
807 {
808     struct json *table_update;
809     struct shash_node *node;
810     struct table t;
811     size_t i;
812
813     table_init(&t);
814
815     if (table_updates->type != JSON_OBJECT) {
816         ovs_error(0, "<table-updates> is not object");
817         return;
818     }
819     table_update = shash_find_data(json_object(table_updates), table->name);
820     if (!table_update) {
821         return;
822     }
823     if (table_update->type != JSON_OBJECT) {
824         ovs_error(0, "<table-update> is not object");
825         return;
826     }
827
828     table_add_column(&t, "row");
829     table_add_column(&t, "action");
830     for (i = 0; i < columns->n_columns; i++) {
831         table_add_column(&t, "%s", columns->columns[i]->name);
832     }
833     SHASH_FOR_EACH (node, json_object(table_update)) {
834         struct json *row_update = node->data;
835         struct json *old, *new;
836
837         if (row_update->type != JSON_OBJECT) {
838             ovs_error(0, "<row-update> is not object");
839             continue;
840         }
841         old = shash_find_data(json_object(row_update), "old");
842         new = shash_find_data(json_object(row_update), "new");
843         if (initial) {
844             monitor_print_row(new, "initial", node->name, columns, &t);
845         } else if (!old) {
846             monitor_print_row(new, "insert", node->name, columns, &t);
847         } else if (!new) {
848             monitor_print_row(old, "delete", node->name, columns, &t);
849         } else {
850             monitor_print_row(old, "old", node->name, columns, &t);
851             monitor_print_row(new, "new", "", columns, &t);
852         }
853     }
854     table_print(&t);
855     table_destroy(&t);
856 }
857
858 static void
859 do_monitor(int argc, char *argv[])
860 {
861     const char *server = argv[1];
862     const char *database = argv[2];
863     const char *table_name = argv[3];
864     struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
865     struct ovsdb_table_schema *table;
866     struct ovsdb_schema *schema;
867     struct jsonrpc_msg *request;
868     struct jsonrpc *rpc;
869     struct json *select, *monitor, *monitor_request, *monitor_requests,
870         *request_id;
871
872     rpc = open_jsonrpc(server);
873
874     schema = fetch_schema_from_rpc(rpc, database);
875     table = shash_find_data(&schema->tables, table_name);
876     if (!table) {
877         ovs_fatal(0, "%s: %s does not have a table named \"%s\"",
878                   server, database, table_name);
879     }
880
881     if (argc >= 5 && *argv[4] != '\0') {
882         char *save_ptr = NULL;
883         char *token;
884
885         for (token = strtok_r(argv[4], ",", &save_ptr); token != NULL;
886              token = strtok_r(NULL, ",", &save_ptr)) {
887             const struct ovsdb_column *column;
888             column = ovsdb_table_schema_get_column(table, token);
889             if (!column) {
890                 ovs_fatal(0, "%s: table \"%s\" in %s does not have a "
891                           "column named \"%s\"",
892                           server, table_name, database, token);
893             }
894             ovsdb_column_set_add(&columns, column);
895         }
896     } else {
897         struct shash_node *node;
898
899         SHASH_FOR_EACH (node, &table->columns) {
900             const struct ovsdb_column *column = node->data;
901             if (column->index != OVSDB_COL_UUID) {
902                 ovsdb_column_set_add(&columns, column);
903             }
904         }
905     }
906
907     if (argc >= 6 && *argv[5] != '\0') {
908         char *save_ptr = NULL;
909         char *token;
910
911         select = json_object_create();
912         for (token = strtok_r(argv[5], ",", &save_ptr); token != NULL;
913              token = strtok_r(NULL, ",", &save_ptr)) {
914             json_object_put(select, token, json_boolean_create(true));
915         }
916     } else {
917         select = NULL;
918     }
919
920     monitor_request = json_object_create();
921     json_object_put(monitor_request,
922                     "columns", ovsdb_column_set_to_json(&columns));
923     if (select) {
924         json_object_put(monitor_request, "select", select);
925     }
926
927     monitor_requests = json_object_create();
928     json_object_put(monitor_requests, table_name, monitor_request);
929
930     monitor = json_array_create_3(json_string_create(database),
931                                   json_null_create(), monitor_requests);
932     request = jsonrpc_create_request("monitor", monitor, NULL);
933     request_id = json_clone(request->id);
934     jsonrpc_send(rpc, request);
935     for (;;) {
936         struct jsonrpc_msg *msg;
937         int error;
938
939         error = jsonrpc_recv_block(rpc, &msg);
940         if (error) {
941             ovsdb_schema_destroy(schema);
942             ovs_fatal(error, "%s: receive failed", server);
943         }
944
945         if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
946             jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params),
947                                                    msg->id));
948         } else if (msg->type == JSONRPC_REPLY
949                    && json_equal(msg->id, request_id)) {
950             monitor_print(msg->result, table, &columns, true);
951             fflush(stdout);
952             if (get_detach()) {
953                 /* daemonize() closes the standard file descriptors.  We output
954                  * to stdout, so we need to save and restore STDOUT_FILENO. */
955                 int fd = dup(STDOUT_FILENO);
956                 daemonize();
957                 dup2(fd, STDOUT_FILENO);
958                 close(fd);
959             }
960         } else if (msg->type == JSONRPC_NOTIFY
961                    && !strcmp(msg->method, "update")) {
962             struct json *params = msg->params;
963             if (params->type == JSON_ARRAY
964                 && params->u.array.n == 2
965                 && params->u.array.elems[0]->type == JSON_NULL) {
966                 monitor_print(params->u.array.elems[1],
967                               table, &columns, false);
968                 fflush(stdout);
969             }
970         }
971         jsonrpc_msg_destroy(msg);
972     }
973 }
974
975 static void
976 do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
977 {
978     usage();
979 }
980
981 static const struct command all_commands[] = {
982     { "list-dbs", 1, 1, do_list_dbs },
983     { "get-schema", 2, 2, do_get_schema },
984     { "list-tables", 2, 2, do_list_tables },
985     { "list-columns", 2, 3, do_list_columns },
986     { "transact", 2, 2, do_transact },
987     { "monitor", 3, 5, do_monitor },
988     { "help", 0, INT_MAX, do_help },
989     { NULL, 0, 0, NULL },
990 };