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