Setting tag sliver-openvswitch-2.2.90-1
[sliver-openvswitch.git] / tests / test-jsonrpc.c
1 /*
2  * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18
19 #include "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-ssl.h"
32 #include "stream.h"
33 #include "timeval.h"
34 #include "util.h"
35 #include "vlog.h"
36 #include "ovstest.h"
37
38 static void usage(void) NO_RETURN;
39 static void parse_options(int argc, char *argv[]);
40 static struct command *get_all_commands(void);
41
42 static void
43 test_jsonrpc_main(int argc, char *argv[])
44 {
45     proctitle_init(argc, argv);
46     set_program_name(argv[0]);
47     parse_options(argc, argv);
48     run_command(argc - optind, argv + optind, get_all_commands());
49 }
50
51 static void
52 parse_options(int argc, char *argv[])
53 {
54     enum {
55         OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1,
56         DAEMON_OPTION_ENUMS
57     };
58     static const struct option long_options[] = {
59         {"verbose", optional_argument, NULL, 'v'},
60         {"help", no_argument, NULL, 'h'},
61         DAEMON_LONG_OPTIONS,
62         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
63         STREAM_SSL_LONG_OPTIONS,
64         {NULL, 0, NULL, 0},
65     };
66     char *short_options = long_options_to_short_options(long_options);
67
68     for (;;) {
69         int c = getopt_long(argc, argv, short_options, long_options, NULL);
70         if (c == -1) {
71             break;
72         }
73
74         switch (c) {
75         case 'h':
76             usage();
77
78         case 'v':
79             vlog_set_verbosity(optarg);
80             break;
81
82         DAEMON_OPTION_HANDLERS
83
84         STREAM_SSL_OPTION_HANDLERS
85
86         case OPT_BOOTSTRAP_CA_CERT:
87             stream_ssl_set_ca_cert_file(optarg, true);
88             break;
89
90         case '?':
91             exit(EXIT_FAILURE);
92
93         default:
94             abort();
95         }
96     }
97     free(short_options);
98 }
99
100 static void
101 usage(void)
102 {
103     printf("%s: JSON-RPC test utility\n"
104            "usage: %s [OPTIONS] COMMAND [ARG...]\n"
105            "  listen LOCAL             listen for connections on LOCAL\n"
106            "  request REMOTE METHOD PARAMS   send request, print reply\n"
107            "  notify REMOTE METHOD PARAMS  send notification and exit\n",
108            program_name, program_name);
109     stream_usage("JSON-RPC", true, true, true);
110     daemon_usage();
111     vlog_usage();
112     printf("\nOther options:\n"
113            "  -h, --help                  display this help message\n");
114     exit(EXIT_SUCCESS);
115 }
116 \f
117 /* Command helper functions. */
118
119 static struct json *
120 parse_json(const char *s)
121 {
122     struct json *json = json_from_string(s);
123     if (json->type == JSON_STRING) {
124         ovs_fatal(0, "\"%s\": %s", s, json->u.string);
125     }
126     return json;
127 }
128
129 static void
130 print_and_free_json(struct json *json)
131 {
132     char *string = json_to_string(json, JSSF_SORT);
133     json_destroy(json);
134     puts(string);
135     free(string);
136 }
137 \f
138 /* Command implementations. */
139
140 static int
141 handle_rpc(struct jsonrpc *rpc, struct jsonrpc_msg *msg, bool *done)
142 {
143     if (msg->type == JSONRPC_REQUEST) {
144         struct jsonrpc_msg *reply = NULL;
145         if (!strcmp(msg->method, "echo")) {
146             reply = jsonrpc_create_reply(json_clone(msg->params), msg->id);
147         } else {
148             struct json *error = json_object_create();
149             json_object_put_string(error, "error", "unknown method");
150             reply = jsonrpc_create_error(error, msg->id);
151             ovs_error(0, "unknown request %s", msg->method);
152         }
153         jsonrpc_send(rpc, reply);
154         return 0;
155     } else if (msg->type == JSONRPC_NOTIFY) {
156         if (!strcmp(msg->method, "shutdown")) {
157             *done = true;
158             return 0;
159         } else {
160             ovs_error(0, "unknown notification %s", msg->method);
161             return ENOTTY;
162         }
163     } else {
164         ovs_error(0, "unsolicited JSON-RPC reply or error");
165         return EPROTO;
166     }
167 }
168
169 static void
170 do_listen(int argc OVS_UNUSED, char *argv[])
171 {
172     struct pstream *pstream;
173     struct jsonrpc **rpcs;
174     size_t n_rpcs, allocated_rpcs;
175     bool done;
176     int error;
177
178     error = jsonrpc_pstream_open(argv[1], &pstream, DSCP_DEFAULT);
179     if (error) {
180         ovs_fatal(error, "could not listen on \"%s\"", argv[1]);
181     }
182
183     daemonize();
184
185     rpcs = NULL;
186     n_rpcs = allocated_rpcs = 0;
187     done = false;
188     for (;;) {
189         struct stream *stream;
190         size_t i;
191
192         /* Accept new connections. */
193         error = pstream_accept(pstream, &stream);
194         if (!error) {
195             if (n_rpcs >= allocated_rpcs) {
196                 rpcs = x2nrealloc(rpcs, &allocated_rpcs, sizeof *rpcs);
197             }
198             rpcs[n_rpcs++] = jsonrpc_open(stream);
199         } else if (error != EAGAIN) {
200             ovs_fatal(error, "pstream_accept failed");
201         }
202
203         /* Service existing connections. */
204         for (i = 0; i < n_rpcs; ) {
205             struct jsonrpc *rpc = rpcs[i];
206             struct jsonrpc_msg *msg;
207
208             jsonrpc_run(rpc);
209             if (!jsonrpc_get_backlog(rpc)) {
210                 error = jsonrpc_recv(rpc, &msg);
211                 if (!error) {
212                     error = handle_rpc(rpc, msg, &done);
213                     jsonrpc_msg_destroy(msg);
214                 } else if (error == EAGAIN) {
215                     error = 0;
216                 }
217             }
218
219             if (!error) {
220                 error = jsonrpc_get_status(rpc);
221             }
222             if (error) {
223                 jsonrpc_close(rpc);
224                 ovs_error(error, "connection closed");
225                 memmove(&rpcs[i], &rpcs[i + 1],
226                         (n_rpcs - i - 1) * sizeof *rpcs);
227                 n_rpcs--;
228             } else {
229                 i++;
230             }
231         }
232
233         /* Wait for something to do. */
234         if (done && !n_rpcs) {
235             break;
236         }
237         pstream_wait(pstream);
238         for (i = 0; i < n_rpcs; i++) {
239             struct jsonrpc *rpc = rpcs[i];
240
241             jsonrpc_wait(rpc);
242             if (!jsonrpc_get_backlog(rpc)) {
243                 jsonrpc_recv_wait(rpc);
244             }
245         }
246         poll_block();
247     }
248     free(rpcs);
249     pstream_close(pstream);
250 }
251
252 static void
253 do_request(int argc OVS_UNUSED, char *argv[])
254 {
255     struct jsonrpc_msg *msg;
256     struct jsonrpc *rpc;
257     struct json *params;
258     struct stream *stream;
259     const char *method;
260     char *string;
261     int error;
262
263     method = argv[2];
264     params = parse_json(argv[3]);
265     msg = jsonrpc_create_request(method, params, NULL);
266     string = jsonrpc_msg_is_valid(msg);
267     if (string) {
268         ovs_fatal(0, "not a valid JSON-RPC request: %s", string);
269     }
270
271     error = stream_open_block(jsonrpc_stream_open(argv[1], &stream,
272                               DSCP_DEFAULT), &stream);
273     if (error) {
274         ovs_fatal(error, "could not open \"%s\"", argv[1]);
275     }
276     rpc = jsonrpc_open(stream);
277
278     error = jsonrpc_send(rpc, msg);
279     if (error) {
280         ovs_fatal(error, "could not send request");
281     }
282
283     error = jsonrpc_recv_block(rpc, &msg);
284     if (error) {
285         ovs_fatal(error, "error waiting for reply");
286     }
287     print_and_free_json(jsonrpc_msg_to_json(msg));
288
289     jsonrpc_close(rpc);
290 }
291
292 static void
293 do_notify(int argc OVS_UNUSED, char *argv[])
294 {
295     struct jsonrpc_msg *msg;
296     struct jsonrpc *rpc;
297     struct json *params;
298     struct stream *stream;
299     const char *method;
300     char *string;
301     int error;
302
303     method = argv[2];
304     params = parse_json(argv[3]);
305     msg = jsonrpc_create_notify(method, params);
306     string = jsonrpc_msg_is_valid(msg);
307     if (string) {
308         ovs_fatal(0, "not a JSON RPC-valid notification: %s", string);
309     }
310
311     error = stream_open_block(jsonrpc_stream_open(argv[1], &stream,
312                               DSCP_DEFAULT), &stream);
313     if (error) {
314         ovs_fatal(error, "could not open \"%s\"", argv[1]);
315     }
316     rpc = jsonrpc_open(stream);
317
318     error = jsonrpc_send_block(rpc, msg);
319     if (error) {
320         ovs_fatal(error, "could not send notification");
321     }
322     jsonrpc_close(rpc);
323 }
324
325 static void
326 do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
327 {
328     usage();
329 }
330
331 static struct command all_commands[] = {
332     { "listen", 1, 1, do_listen },
333     { "request", 3, 3, do_request },
334     { "notify", 3, 3, do_notify },
335     { "help", 0, INT_MAX, do_help },
336     { NULL, 0, 0, NULL },
337 };
338
339 static struct command *
340 get_all_commands(void)
341 {
342     return all_commands;
343 }
344
345 OVSTEST_REGISTER("test-jsonrpc", test_jsonrpc_main);