Coverage Report

Created: 2023-03-26 07:41

/src/openvswitch/lib/unixctl.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 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
#include "unixctl.h"
19
#include <errno.h>
20
#include <unistd.h>
21
#include "coverage.h"
22
#include "dirs.h"
23
#include "openvswitch/dynamic-string.h"
24
#include "openvswitch/json.h"
25
#include "jsonrpc.h"
26
#include "openvswitch/list.h"
27
#include "openvswitch/poll-loop.h"
28
#include "openvswitch/shash.h"
29
#include "stream.h"
30
#include "stream-provider.h"
31
#include "svec.h"
32
#include "openvswitch/vlog.h"
33
34
VLOG_DEFINE_THIS_MODULE(unixctl);
35
36
COVERAGE_DEFINE(unixctl_received);
37
COVERAGE_DEFINE(unixctl_replied);
38

39
struct unixctl_command {
40
    const char *usage;
41
    int min_args, max_args;
42
    unixctl_cb_func *cb;
43
    void *aux;
44
};
45
46
struct unixctl_conn {
47
    struct ovs_list node;
48
    struct jsonrpc *rpc;
49
50
    /* Only one request can be in progress at a time.  While the request is
51
     * being processed, 'request_id' is populated, otherwise it is null. */
52
    struct json *request_id;   /* ID of the currently active request. */
53
};
54
55
/* Server for control connection. */
56
struct unixctl_server {
57
    struct pstream *listener;
58
    struct ovs_list conns;
59
    char *path;
60
};
61
62
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
63
64
static struct shash commands = SHASH_INITIALIZER(&commands);
65
66
static void
67
unixctl_list_commands(struct unixctl_conn *conn, int argc OVS_UNUSED,
68
                      const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
69
0
{
70
0
    struct ds ds = DS_EMPTY_INITIALIZER;
71
0
    const struct shash_node **nodes = shash_sort(&commands);
72
0
    size_t i;
73
74
0
    ds_put_cstr(&ds, "The available commands are:\n");
75
76
0
    for (i = 0; i < shash_count(&commands); i++) {
77
0
        const struct shash_node *node = nodes[i];
78
0
        const struct unixctl_command *command = node->data;
79
80
0
        if (command->usage) {
81
0
            ds_put_format(&ds, "  %-23s %s\n", node->name, command->usage);
82
0
        }
83
0
    }
84
0
    free(nodes);
85
86
0
    unixctl_command_reply(conn, ds_cstr(&ds));
87
0
    ds_destroy(&ds);
88
0
}
89
90
static void
91
unixctl_version(struct unixctl_conn *conn, int argc OVS_UNUSED,
92
                const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
93
0
{
94
0
    unixctl_command_reply(conn, ovs_get_program_version());
95
0
}
96
97
/* Registers a unixctl command with the given 'name'.  'usage' describes the
98
 * arguments to the command; it is used only for presentation to the user in
99
 * "list-commands" output.  (If 'usage' is NULL, then the command is hidden.)
100
 *
101
 * 'cb' is called when the command is received.  It is passed an array
102
 * containing the command name and arguments, plus a copy of 'aux'.  Normally
103
 * 'cb' should reply by calling unixctl_command_reply() or
104
 * unixctl_command_reply_error() before it returns, but if the command cannot
105
 * be handled immediately then it can defer the reply until later.  A given
106
 * connection can only process a single request at a time, so a reply must be
107
 * made eventually to avoid blocking that connection. */
108
void
109
unixctl_command_register(const char *name, const char *usage,
110
                         int min_args, int max_args,
111
                         unixctl_cb_func *cb, void *aux)
112
0
{
113
0
    struct unixctl_command *command;
114
0
    struct unixctl_command *lookup = shash_find_data(&commands, name);
115
116
0
    ovs_assert(!lookup || lookup->cb == cb);
117
118
0
    if (lookup) {
119
0
        return;
120
0
    }
121
122
0
    command = xmalloc(sizeof *command);
123
0
    command->usage = usage;
124
0
    command->min_args = min_args;
125
0
    command->max_args = max_args;
126
0
    command->cb = cb;
127
0
    command->aux = aux;
128
0
    shash_add(&commands, name, command);
129
0
}
130
131
static void
132
unixctl_command_reply__(struct unixctl_conn *conn,
133
                        bool success, const char *body)
134
0
{
135
0
    struct json *body_json;
136
0
    struct jsonrpc_msg *reply;
137
138
0
    COVERAGE_INC(unixctl_replied);
139
0
    ovs_assert(conn->request_id);
140
141
0
    if (!body) {
142
0
        body = "";
143
0
    }
144
145
0
    if (body[0] && body[strlen(body) - 1] != '\n') {
146
0
        body_json = json_string_create_nocopy(xasprintf("%s\n", body));
147
0
    } else {
148
0
        body_json = json_string_create(body);
149
0
    }
150
151
0
    if (success) {
152
0
        reply = jsonrpc_create_reply(body_json, conn->request_id);
153
0
    } else {
154
0
        reply = jsonrpc_create_error(body_json, conn->request_id);
155
0
    }
156
157
0
    if (VLOG_IS_DBG_ENABLED()) {
158
0
        char *id = json_to_string(conn->request_id, 0);
159
0
        VLOG_DBG("replying with %s, id=%s: \"%s\"",
160
0
                 success ? "success" : "error", id, body);
161
0
        free(id);
162
0
    }
163
164
    /* If jsonrpc_send() returns an error, the run loop will take care of the
165
     * problem eventually. */
166
0
    jsonrpc_send(conn->rpc, reply);
167
0
    json_destroy(conn->request_id);
168
0
    conn->request_id = NULL;
169
0
}
170
171
/* Replies to the active unixctl connection 'conn'.  'result' is sent to the
172
 * client indicating the command was processed successfully.  Only one call to
173
 * unixctl_command_reply() or unixctl_command_reply_error() may be made per
174
 * request. */
175
void
176
unixctl_command_reply(struct unixctl_conn *conn, const char *result)
177
0
{
178
0
    unixctl_command_reply__(conn, true, result);
179
0
}
180
181
/* Replies to the active unixctl connection 'conn'. 'error' is sent to the
182
 * client indicating an error occurred processing the command.  Only one call to
183
 * unixctl_command_reply() or unixctl_command_reply_error() may be made per
184
 * request. */
185
void
186
unixctl_command_reply_error(struct unixctl_conn *conn, const char *error)
187
0
{
188
0
    unixctl_command_reply__(conn, false, error);
189
0
}
190
191
/* Creates a unixctl server listening on 'path', which for POSIX may be:
192
 *
193
 *      - NULL, in which case <rundir>/<program>.<pid>.ctl is used.
194
 *
195
 *      - A name that does not start with '/', in which case it is put in
196
 *        <rundir>.
197
 *
198
 *      - An absolute path (starting with '/') that gives the exact name of
199
 *        the Unix domain socket to listen on.
200
 *
201
 * For Windows, a local named pipe is used. A file is created in 'path'
202
 * which may be:
203
 *
204
 *      - NULL, in which case <rundir>/<program>.ctl is used.
205
 *
206
 *      - An absolute path that gives the name of the file.
207
 *
208
 * For both POSIX and Windows, if the path is "none", the function will
209
 * return successfully but no socket will actually be created.
210
 *
211
 * A program that (optionally) daemonizes itself should call this function
212
 * *after* daemonization, so that the socket name contains the pid of the
213
 * daemon instead of the pid of the program that exited.  (Otherwise,
214
 * "ovs-appctl --target=<program>" will fail.)
215
 *
216
 * Returns 0 if successful, otherwise a positive errno value.  If successful,
217
 * sets '*serverp' to the new unixctl_server (or to NULL if 'path' was "none"),
218
 * otherwise to NULL. */
219
int
220
unixctl_server_create(const char *path, struct unixctl_server **serverp)
221
0
{
222
0
    *serverp = NULL;
223
0
    if (path && !strcmp(path, "none")) {
224
0
        return 0;
225
0
    }
226
227
#ifdef _WIN32
228
    enum { WINDOWS = 1 };
229
#else
230
0
    enum { WINDOWS = 0 };
231
0
#endif
232
233
0
    long int pid = getpid();
234
0
    char *abs_path
235
0
        = (path ? abs_file_name(ovs_rundir(), path)
236
0
           : WINDOWS ? xasprintf("%s/%s.ctl", ovs_rundir(), program_name)
237
0
           : xasprintf("%s/%s.%ld.ctl", ovs_rundir(), program_name, pid));
238
239
0
    struct pstream *listener;
240
0
    char *punix_path = xasprintf("punix:%s", abs_path);
241
0
    int error = pstream_open(punix_path, &listener, 0);
242
0
    free(punix_path);
243
244
0
    if (error) {
245
0
        ovs_error(error, "%s: could not initialize control socket", abs_path);
246
0
        free(abs_path);
247
0
        return error;
248
0
    }
249
250
0
    unixctl_command_register("list-commands", "", 0, 0, unixctl_list_commands,
251
0
                             NULL);
252
0
    unixctl_command_register("version", "", 0, 0, unixctl_version, NULL);
253
254
0
    struct unixctl_server *server = xmalloc(sizeof *server);
255
0
    server->listener = listener;
256
0
    server->path = abs_path;
257
0
    ovs_list_init(&server->conns);
258
0
    *serverp = server;
259
0
    return 0;
260
0
}
261
262
static void
263
process_command(struct unixctl_conn *conn, struct jsonrpc_msg *request)
264
0
{
265
0
    char *error = NULL;
266
267
0
    struct unixctl_command *command;
268
0
    struct json_array *params;
269
270
0
    COVERAGE_INC(unixctl_received);
271
0
    conn->request_id = json_clone(request->id);
272
273
0
    if (VLOG_IS_DBG_ENABLED()) {
274
0
        char *params_s = json_to_string(request->params, 0);
275
0
        char *id_s = json_to_string(request->id, 0);
276
0
        VLOG_DBG("received request %s%s, id=%s",
277
0
                 request->method, params_s, id_s);
278
0
        free(params_s);
279
0
        free(id_s);
280
0
    }
281
282
0
    params = json_array(request->params);
283
0
    command = shash_find_data(&commands, request->method);
284
0
    if (!command) {
285
0
        error = xasprintf("\"%s\" is not a valid command (use "
286
0
                          "\"list-commands\" to see a list of valid commands)",
287
0
                          request->method);
288
0
    } else if (params->n < command->min_args) {
289
0
        error = xasprintf("\"%s\" command requires at least %d arguments",
290
0
                          request->method, command->min_args);
291
0
    } else if (params->n > command->max_args) {
292
0
        error = xasprintf("\"%s\" command takes at most %d arguments",
293
0
                          request->method, command->max_args);
294
0
    } else {
295
0
        struct svec argv = SVEC_EMPTY_INITIALIZER;
296
0
        int  i;
297
298
0
        svec_add(&argv, request->method);
299
0
        for (i = 0; i < params->n; i++) {
300
0
            if (params->elems[i]->type != JSON_STRING) {
301
0
                error = xasprintf("\"%s\" command has non-string argument",
302
0
                                  request->method);
303
0
                break;
304
0
            }
305
0
            svec_add(&argv, json_string(params->elems[i]));
306
0
        }
307
0
        svec_terminate(&argv);
308
309
0
        if (!error) {
310
0
            command->cb(conn, argv.n, (const char **) argv.names,
311
0
                        command->aux);
312
0
        }
313
314
0
        svec_destroy(&argv);
315
0
    }
316
317
0
    if (error) {
318
0
        unixctl_command_reply_error(conn, error);
319
0
        free(error);
320
0
    }
321
0
}
322
323
static int
324
run_connection(struct unixctl_conn *conn)
325
0
{
326
0
    int error, i;
327
328
0
    jsonrpc_run(conn->rpc);
329
0
    error = jsonrpc_get_status(conn->rpc);
330
0
    if (error || jsonrpc_get_backlog(conn->rpc)) {
331
0
        return error;
332
0
    }
333
334
0
    for (i = 0; i < 10; i++) {
335
0
        struct jsonrpc_msg *msg;
336
337
0
        if (error || conn->request_id) {
338
0
            break;
339
0
        }
340
341
0
        jsonrpc_recv(conn->rpc, &msg);
342
0
        if (msg) {
343
0
            if (msg->type == JSONRPC_REQUEST) {
344
0
                process_command(conn, msg);
345
0
            } else {
346
0
                VLOG_WARN_RL(&rl, "%s: received unexpected %s message",
347
0
                             jsonrpc_get_name(conn->rpc),
348
0
                             jsonrpc_msg_type_to_string(msg->type));
349
0
                error = EINVAL;
350
0
            }
351
0
            jsonrpc_msg_destroy(msg);
352
0
        }
353
0
        error = error ? error : jsonrpc_get_status(conn->rpc);
354
0
    }
355
356
0
    return error;
357
0
}
358
359
static void
360
kill_connection(struct unixctl_conn *conn)
361
0
{
362
0
    ovs_list_remove(&conn->node);
363
0
    jsonrpc_close(conn->rpc);
364
0
    json_destroy(conn->request_id);
365
0
    free(conn);
366
0
}
367
368
void
369
unixctl_server_run(struct unixctl_server *server)
370
0
{
371
0
    if (!server) {
372
0
        return;
373
0
    }
374
375
0
    for (int i = 0; i < 10; i++) {
376
0
        struct stream *stream;
377
0
        int error;
378
379
0
        error = pstream_accept(server->listener, &stream);
380
0
        if (!error) {
381
0
            struct unixctl_conn *conn = xzalloc(sizeof *conn);
382
0
            ovs_list_push_back(&server->conns, &conn->node);
383
0
            conn->rpc = jsonrpc_open(stream);
384
0
        } else if (error == EAGAIN) {
385
0
            break;
386
0
        } else {
387
0
            VLOG_WARN_RL(&rl, "%s: accept failed: %s",
388
0
                         pstream_get_name(server->listener),
389
0
                         ovs_strerror(error));
390
0
        }
391
0
    }
392
393
0
    struct unixctl_conn *conn;
394
0
    LIST_FOR_EACH_SAFE (conn, node, &server->conns) {
395
0
        int error = run_connection(conn);
396
0
        if (error && error != EAGAIN) {
397
0
            kill_connection(conn);
398
0
        }
399
0
    }
400
0
}
401
402
void
403
unixctl_server_wait(struct unixctl_server *server)
404
0
{
405
0
    struct unixctl_conn *conn;
406
407
0
    if (!server) {
408
0
        return;
409
0
    }
410
411
0
    pstream_wait(server->listener);
412
0
    LIST_FOR_EACH (conn, node, &server->conns) {
413
0
        jsonrpc_wait(conn->rpc);
414
0
        if (!jsonrpc_get_backlog(conn->rpc) && !conn->request_id) {
415
0
            jsonrpc_recv_wait(conn->rpc);
416
0
        }
417
0
    }
418
0
}
419
420
/* Destroys 'server' and stops listening for connections. */
421
void
422
unixctl_server_destroy(struct unixctl_server *server)
423
0
{
424
0
    if (server) {
425
0
        struct unixctl_conn *conn;
426
427
0
        LIST_FOR_EACH_SAFE (conn, node, &server->conns) {
428
0
            kill_connection(conn);
429
0
        }
430
431
0
        free(server->path);
432
0
        pstream_close(server->listener);
433
0
        free(server);
434
0
    }
435
0
}
436
437
const char *
438
unixctl_server_get_path(const struct unixctl_server *server)
439
0
{
440
0
    return server ? server->path : NULL;
441
0
}
442

443
/* On POSIX based systems, connects to a unixctl server socket.  'path' should
444
 * be the name of a unixctl server socket.  If it does not start with '/', it
445
 * will be prefixed with the rundir (e.g. /usr/local/var/run/openvswitch).
446
 *
447
 * On Windows, connects to a local named pipe. A file which resides in
448
 * 'path' is used to mimic the behavior of a Unix domain socket.
449
 * 'path' should be an absolute path of the file.
450
 *
451
 * Returns 0 if successful, otherwise a positive errno value.  If successful,
452
 * sets '*client' to the new jsonrpc, otherwise to NULL. */
453
int
454
unixctl_client_create(const char *path, struct jsonrpc **client)
455
0
{
456
0
    struct stream *stream;
457
0
    int error;
458
459
0
    char *abs_path = abs_file_name(ovs_rundir(), path);
460
0
    char *unix_path = xasprintf("unix:%s", abs_path);
461
462
0
    *client = NULL;
463
464
0
    error = stream_open_block(stream_open(unix_path, &stream, DSCP_DEFAULT),
465
0
                              -1, &stream);
466
0
    free(unix_path);
467
0
    free(abs_path);
468
469
0
    if (error) {
470
0
        VLOG_WARN("failed to connect to %s", path);
471
0
        return error;
472
0
    }
473
474
0
    *client = jsonrpc_open(stream);
475
0
    return 0;
476
0
}
477
478
/* Executes 'command' on the server with an argument vector 'argv' containing
479
 * 'argc' elements.  If successfully communicated with the server, returns 0
480
 * and sets '*result', or '*err' (not both) to the result or error the server
481
 * returned.  Otherwise, sets '*result' and '*err' to NULL and returns a
482
 * positive errno value.  The caller is responsible for freeing '*result' or
483
 * '*err' if not NULL. */
484
int
485
unixctl_client_transact(struct jsonrpc *client, const char *command, int argc,
486
                        char *argv[], char **result, char **err)
487
0
{
488
0
    struct jsonrpc_msg *request, *reply;
489
0
    struct json **json_args, *params;
490
0
    int error, i;
491
492
0
    *result = NULL;
493
0
    *err = NULL;
494
495
0
    json_args = xmalloc(argc * sizeof *json_args);
496
0
    for (i = 0; i < argc; i++) {
497
0
        json_args[i] = json_string_create(argv[i]);
498
0
    }
499
0
    params = json_array_create(json_args, argc);
500
0
    request = jsonrpc_create_request(command, params, NULL);
501
502
0
    error = jsonrpc_transact_block(client, request, &reply);
503
0
    if (error) {
504
0
        VLOG_WARN("error communicating with %s: %s", jsonrpc_get_name(client),
505
0
                  ovs_retval_to_string(error));
506
0
        return error;
507
0
    }
508
509
0
    if (reply->error) {
510
0
        if (reply->error->type == JSON_STRING) {
511
0
            *err = xstrdup(json_string(reply->error));
512
0
        } else {
513
0
            VLOG_WARN("%s: unexpected error type in JSON RPC reply: %s",
514
0
                      jsonrpc_get_name(client),
515
0
                      json_type_to_string(reply->error->type));
516
0
            error = EINVAL;
517
0
        }
518
0
    } else if (reply->result) {
519
0
        if (reply->result->type == JSON_STRING) {
520
0
            *result = xstrdup(json_string(reply->result));
521
0
        } else {
522
0
            VLOG_WARN("%s: unexpected result type in JSON rpc reply: %s",
523
0
                      jsonrpc_get_name(client),
524
0
                      json_type_to_string(reply->result->type));
525
0
            error = EINVAL;
526
0
        }
527
0
    }
528
529
0
    jsonrpc_msg_destroy(reply);
530
0
    return error;
531
0
}