GCC Code Coverage Report


Directory: src/
File: src/command.c
Date: 2024-04-25 03:45:42
Exec Total Coverage
Lines: 89 120 74.2%
Branches: 33 66 50.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2023 Egor Tensin <egor@tensin.name>
3 * This file is part of the "cimple" project.
4 * For details, see https://github.com/egor-tensin/cimple.
5 * Distributed under the MIT License.
6 */
7
8 #include "command.h"
9 #include "compiler.h"
10 #include "event_loop.h"
11 #include "json_rpc.h"
12 #include "log.h"
13
14 #include <poll.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 struct cmd_dispatcher {
19 struct cmd_desc *cmds;
20 size_t numof_cmds;
21 void *ctx;
22 };
23
24 170 static int copy_cmd(struct cmd_desc *dest, const struct cmd_desc *src)
25 {
26 170 dest->name = strdup(src->name);
27
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 170 times.
170 if (!dest->name) {
28 log_errno("strdup");
29 return -1;
30 }
31 170 dest->handler = src->handler;
32 170 return 0;
33 }
34
35 170 static void free_cmd(struct cmd_desc *desc)
36 {
37 170 free(desc->name);
38 170 }
39
40 83 static int copy_cmds(struct cmd_desc *dest, const struct cmd_desc *src, size_t numof_cmds)
41 {
42 83 size_t numof_copied = 0;
43 83 int ret = 0;
44
45
2/2
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 83 times.
253 for (numof_copied = 0; numof_copied < numof_cmds; ++numof_copied) {
46 170 ret = copy_cmd(&dest[numof_copied], &src[numof_copied]);
47
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 170 times.
170 if (ret < 0)
48 goto free;
49 }
50
51 83 return 0;
52
53 free:
54 for (size_t i = 0; i < numof_copied; ++i)
55 free_cmd(&dest[numof_copied]);
56
57 return -1;
58 }
59
60 83 static void free_cmds(struct cmd_desc *cmds, size_t numof_cmds)
61 {
62
2/2
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 83 times.
253 for (size_t i = 0; i < numof_cmds; ++i)
63 170 free_cmd(&cmds[i]);
64 83 }
65
66 83 int cmd_dispatcher_create(struct cmd_dispatcher **_dispatcher, struct cmd_desc *cmds,
67 size_t numof_cmds, void *ctx)
68 {
69 83 int ret = 0;
70
71 83 struct cmd_dispatcher *dispatcher = malloc(sizeof(struct cmd_dispatcher));
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 if (!dispatcher) {
73 log_errno("malloc");
74 return -1;
75 }
76
77 83 dispatcher->ctx = ctx;
78
79 83 dispatcher->cmds = malloc(sizeof(struct cmd_desc) * numof_cmds);
80
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 if (!dispatcher->cmds) {
81 log_errno("malloc");
82 goto free;
83 }
84 83 dispatcher->numof_cmds = numof_cmds;
85
86 83 ret = copy_cmds(dispatcher->cmds, cmds, numof_cmds);
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 if (ret < 0)
88 goto free_cmds;
89
90 83 *_dispatcher = dispatcher;
91 83 return 0;
92
93 free_cmds:
94 free(dispatcher->cmds);
95
96 free:
97 free(dispatcher);
98
99 return -1;
100 }
101
102 83 void cmd_dispatcher_destroy(struct cmd_dispatcher *dispatcher)
103 {
104 83 free_cmds(dispatcher->cmds, dispatcher->numof_cmds);
105 83 free(dispatcher->cmds);
106 83 free(dispatcher);
107 83 }
108
109 36800 static int cmd_dispatcher_handle_internal(const struct cmd_dispatcher *dispatcher,
110 const struct jsonrpc_request *request,
111 struct jsonrpc_response **result, void *arg)
112 {
113 36800 const char *actual_cmd = jsonrpc_request_get_method(request);
114
115
1/2
✓ Branch 0 taken 64418 times.
✗ Branch 1 not taken.
64418 for (size_t i = 0; i < dispatcher->numof_cmds; ++i) {
116 64418 struct cmd_desc *cmd = &dispatcher->cmds[i];
117
118
2/2
✓ Branch 0 taken 27618 times.
✓ Branch 1 taken 36800 times.
64418 if (strcmp(cmd->name, actual_cmd))
119 27618 continue;
120
121 36800 return cmd->handler(request, result, arg);
122 }
123
124 log_err("Received an unknown command: %s\n", actual_cmd);
125 return -1;
126 }
127
128 int cmd_dispatcher_handle(const struct cmd_dispatcher *dispatcher,
129 const struct jsonrpc_request *command, struct jsonrpc_response **result)
130 {
131 return cmd_dispatcher_handle_internal(dispatcher, command, result, dispatcher->ctx);
132 }
133
134 36800 static struct cmd_conn_ctx *make_conn_ctx(int fd, void *arg)
135 {
136 36800 struct cmd_conn_ctx *ctx = malloc(sizeof(struct cmd_conn_ctx));
137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (!ctx) {
138 log_errno("malloc");
139 return NULL;
140 }
141
142 36800 ctx->fd = fd;
143 36800 ctx->arg = arg;
144
145 36800 return ctx;
146 }
147
148 36800 static int cmd_dispatcher_handle_conn_internal(int conn_fd, struct cmd_dispatcher *dispatcher)
149 {
150 36800 int ret = 0;
151
152 36800 struct cmd_conn_ctx *new_ctx = make_conn_ctx(conn_fd, dispatcher->ctx);
153
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (!new_ctx)
154 return -1;
155
156 36800 struct jsonrpc_request *request = NULL;
157 36800 ret = jsonrpc_request_recv(&request, conn_fd);
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36800 times.
36800 if (ret < 0)
159 goto free_ctx;
160
161 36800 const int requires_response = !jsonrpc_request_is_notification(request);
162
163 36800 struct jsonrpc_response *default_response = NULL;
164
2/2
✓ Branch 0 taken 9206 times.
✓ Branch 1 taken 27594 times.
36800 if (requires_response) {
165 9206 ret = jsonrpc_response_create(&default_response, request, NULL);
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
167 goto free_request;
168 }
169
170 36800 struct jsonrpc_response *default_error = NULL;
171
2/2
✓ Branch 0 taken 9206 times.
✓ Branch 1 taken 27594 times.
36800 if (requires_response) {
172 9206 ret = jsonrpc_error_create(&default_error, request, -1, "An error occured");
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (ret < 0)
174 goto free_default_response;
175 }
176
177 36800 struct jsonrpc_response *response = NULL;
178 36800 ret = cmd_dispatcher_handle_internal(dispatcher, request, &response, new_ctx);
179
180
2/2
✓ Branch 0 taken 9206 times.
✓ Branch 1 taken 27594 times.
36800 if (requires_response) {
181 9206 struct jsonrpc_response *actual_response = response;
182
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
9206 if (!actual_response) {
183 actual_response = ret < 0 ? default_error : default_response;
184 }
185
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9206 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
9206 if (ret < 0 && !jsonrpc_response_is_error(actual_response)) {
186 actual_response = default_error;
187 }
188
1/2
✓ Branch 1 taken 9206 times.
✗ Branch 2 not taken.
9206 ret = jsonrpc_response_send(actual_response, conn_fd) < 0 ? -1 : ret;
189 }
190
191
2/2
✓ Branch 0 taken 9206 times.
✓ Branch 1 taken 27594 times.
36800 if (response)
192 9206 jsonrpc_response_destroy(response);
193
194
2/2
✓ Branch 0 taken 27594 times.
✓ Branch 1 taken 9206 times.
36800 if (default_error)
195 9206 jsonrpc_response_destroy(default_error);
196
197 27594 free_default_response:
198
2/2
✓ Branch 0 taken 27594 times.
✓ Branch 1 taken 9206 times.
36800 if (default_response)
199 9206 jsonrpc_response_destroy(default_response);
200
201 27594 free_request:
202 36800 jsonrpc_request_destroy(request);
203
204 36800 free_ctx:
205 36800 free(new_ctx);
206
207 36800 return ret;
208 }
209
210 27620 int cmd_dispatcher_handle_conn(int conn_fd, void *_dispatcher)
211 {
212 27620 return cmd_dispatcher_handle_conn_internal(conn_fd, (struct cmd_dispatcher *)_dispatcher);
213 }
214
215 9180 int cmd_dispatcher_handle_event(UNUSED struct event_loop *loop, int fd, short revents,
216 void *_dispatcher)
217 {
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
9180 if (!(revents & POLLIN)) {
219 log_err("Descriptor %d is not readable\n", fd);
220 return -1;
221 }
222
223 9180 return cmd_dispatcher_handle_conn_internal(fd, (struct cmd_dispatcher *)_dispatcher);
224 }
225