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