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 |