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