Implement JSON-RPC protocol.
[sliver-openvswitch.git] / tests / test-jsonrpc.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 "jsonrpc.h"
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <getopt.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include "command-line.h"
28 #include "daemon.h"
29 #include "json.h"
30 #include "poll-loop.h"
31 #include "stream.h"
32 #include "timeval.h"
33 #include "util.h"
34 #include "vlog.h"
35
36 static struct command all_commands[];
37
38 static void usage(void) NO_RETURN;
39 static void parse_options(int argc, char *argv[]);
40
41 int
42 main(int argc, char *argv[])
43 {
44     set_program_name(argv[0]);
45     time_init();
46     vlog_init();
47     parse_options(argc, argv);
48     run_command(argc - optind, argv + optind, all_commands);
49     return 0;
50 }
51
52 static void
53 parse_options(int argc, char *argv[])
54 {
55     static struct option long_options[] = {
56         {"verbose", optional_argument, 0, 'v'},
57         {"help", no_argument, 0, 'h'},
58         DAEMON_LONG_OPTIONS,
59         {0, 0, 0, 0},
60     };
61     char *short_options = long_options_to_short_options(long_options);
62
63     for (;;) {
64         int c = getopt_long(argc, argv, short_options, long_options, NULL);
65         if (c == -1) {
66             break;
67         }
68
69         switch (c) {
70         case 'h':
71             usage();
72
73         case 'v':
74             vlog_set_verbosity(optarg);
75             break;
76
77         DAEMON_OPTION_HANDLERS
78
79         case '?':
80             exit(EXIT_FAILURE);
81
82         default:
83             abort();
84         }
85     }
86     free(short_options);
87 }
88
89 static void
90 usage(void)
91 {
92     printf("%s: JSON-RPC test utility\n"
93            "usage: %s [OPTIONS] COMMAND [ARG...]\n"
94            "  listen LOCAL             listen for connections on LOCAL\n"
95            "  request REMOTE METHOD PARAMS   send request, print reply\n"
96            "  notify REMOTE METHOD PARAMS  send notification and exit\n",
97            program_name, program_name);
98     stream_usage("JSON-RPC", true, true);
99     daemon_usage();
100     vlog_usage();
101     printf("\nOther options:\n"
102            "  -h, --help                  display this help message\n");
103     exit(EXIT_SUCCESS);
104 }
105 \f
106 /* Command helper functions. */
107
108 static struct json *
109 parse_json(const char *s)
110 {
111     struct json *json = json_from_string(s);
112     if (json->type == JSON_STRING) {
113         ovs_fatal(0, "\"%s\": %s", s, json->u.string);
114     }
115     return json;
116 }
117
118 static void
119 print_and_free_json(struct json *json)
120 {
121     char *string = json_to_string(json, JSSF_SORT);
122     json_destroy(json);
123     puts(string);
124     free(string);
125 }
126 \f
127 /* Command implementations. */
128
129 static void
130 handle_rpc(struct jsonrpc *rpc, struct jsonrpc_msg *msg, bool *done)
131 {
132     struct jsonrpc_msg *reply = NULL;
133     if (msg->type == JSONRPC_REQUEST) {
134         if (!strcmp(msg->method, "echo")) {
135             reply = jsonrpc_create_reply(json_clone(msg->params), msg->id);
136         } else {
137             struct json *error = json_object_create();
138             json_object_put_string(error, "error", "unknown method");
139             reply = jsonrpc_create_error(error, msg->id);
140             ovs_error(0, "unknown request %s", msg->method);
141         }
142
143     } else if (msg->type == JSONRPC_NOTIFY) {
144         if (!strcmp(msg->method, "shutdown")) {
145             *done = true;
146         } else {
147             jsonrpc_error(rpc, ENOTTY);
148             ovs_error(0, "unknown notification %s", msg->method);
149         }
150     } else {
151         jsonrpc_error(rpc, EPROTO);
152         ovs_error(0, "unsolicited JSON-RPC reply or error");
153     }
154
155     if (reply) {
156         jsonrpc_send(rpc, reply);
157     }
158 }
159
160 static void
161 do_listen(int argc UNUSED, char *argv[])
162 {
163     struct pstream *pstream;
164     struct jsonrpc **rpcs;
165     size_t n_rpcs, allocated_rpcs;
166     bool done;
167     int error;
168
169     die_if_already_running();
170
171     error = pstream_open(argv[1], &pstream);
172     if (error) {
173         ovs_fatal(error, "could not listen on \"%s\"", argv[1]);
174     }
175
176     daemonize();
177
178     rpcs = NULL;
179     n_rpcs = allocated_rpcs = 0;
180     done = false;
181     for (;;) {
182         struct stream *stream;
183         size_t i;
184
185         /* Accept new connections. */
186         error = pstream_accept(pstream, &stream);
187         if (!error) {
188             if (n_rpcs >= allocated_rpcs) {
189                 rpcs = x2nrealloc(rpcs, &allocated_rpcs, sizeof *rpcs);
190             }
191             rpcs[n_rpcs++] = jsonrpc_open(stream);
192         } else if (error != EAGAIN) {
193             ovs_fatal(error, "pstream_accept failed");
194         }
195
196         /* Service existing connections. */
197         for (i = 0; i < n_rpcs; ) {
198             struct jsonrpc *rpc = rpcs[i];
199             struct jsonrpc_msg *msg;
200
201             jsonrpc_run(rpc);
202             if (!jsonrpc_get_backlog(rpc)) {
203                 error = jsonrpc_recv(rpc, &msg);
204                 if (!error) {
205                     handle_rpc(rpc, msg, &done);
206                     jsonrpc_msg_destroy(msg);
207                 }
208             }
209
210             error = jsonrpc_get_status(rpc);
211             if (error) {
212                 jsonrpc_close(rpc);
213                 ovs_error(error, "connection closed");
214                 memmove(&rpcs[i], &rpcs[i + 1],
215                         (n_rpcs - i - 1) * sizeof *rpcs);
216                 n_rpcs--;
217             } else {
218                 i++;
219             }
220         }
221
222         /* Wait for something to do. */
223         if (done && !n_rpcs) {
224             break;
225         }
226         pstream_wait(pstream);
227         for (i = 0; i < n_rpcs; i++) {
228             struct jsonrpc *rpc = rpcs[i];
229
230             jsonrpc_wait(rpc);
231             if (!jsonrpc_get_backlog(rpc)) {
232                 jsonrpc_recv_wait(rpc);
233             }
234         }
235         poll_block();
236     }
237 }
238
239
240 static void
241 do_request(int argc UNUSED, char *argv[])
242 {
243     struct jsonrpc_msg *msg;
244     struct jsonrpc *rpc;
245     struct json *params;
246     struct stream *stream;
247     const char *method;
248     char *string;
249     int error;
250
251     method = argv[2];
252     params = parse_json(argv[3]);
253     msg = jsonrpc_create_request(method, params);
254     string = jsonrpc_msg_is_valid(msg);
255     if (string) {
256         ovs_fatal(0, "not a valid JSON-RPC request: %s", string);
257     }
258
259     error = stream_open_block(argv[1], &stream);
260     if (error) {
261         ovs_fatal(error, "could not open \"%s\"", argv[1]);
262     }
263     rpc = jsonrpc_open(stream);
264
265     error = jsonrpc_send(rpc, msg);
266     if (error) {
267         ovs_fatal(error, "could not send request");
268     }
269
270     error = jsonrpc_recv_block(rpc, &msg);
271     if (error) {
272         ovs_fatal(error, "error waiting for reply");
273     }
274     print_and_free_json(jsonrpc_msg_to_json(msg));
275
276     jsonrpc_close(rpc);
277 }
278
279 static void
280 do_notify(int argc UNUSED, char *argv[])
281 {
282     struct jsonrpc_msg *msg;
283     struct jsonrpc *rpc;
284     struct json *params;
285     struct stream *stream;
286     const char *method;
287     char *string;
288     int error;
289
290     method = argv[2];
291     params = parse_json(argv[3]);
292     msg = jsonrpc_create_notify(method, params);
293     string = jsonrpc_msg_is_valid(msg);
294     if (string) {
295         ovs_fatal(0, "not a JSON RPC-valid notification: %s", string);
296     }
297
298     error = stream_open_block(argv[1], &stream);
299     if (error) {
300         ovs_fatal(error, "could not open \"%s\"", argv[1]);
301     }
302     rpc = jsonrpc_open(stream);
303
304     error = jsonrpc_send_block(rpc, msg);
305     if (error) {
306         ovs_fatal(error, "could not send request");
307     }
308     jsonrpc_close(rpc);
309 }
310
311 static void
312 do_help(int argc UNUSED, char *argv[] UNUSED)
313 {
314     usage();
315 }
316
317 static struct command all_commands[] = {
318     { "listen", 1, 1, do_listen },
319     { "request", 3, 3, do_request },
320     { "notify", 3, 3, do_notify },
321     { "help", 0, INT_MAX, do_help },
322     { NULL, 0, 0, NULL },
323 };