]> git.meshlink.io Git - meshlink/blob - src/devtools.c
Avoid allocating packet buffers unnecessarily.
[meshlink] / src / devtools.c
1 /*
2     devtools.c -- Debugging and quality control functions.
3     Copyright (C) 2014, 2017 Guus Sliepen <guus@meshlink.io>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21 #include <assert.h>
22
23 #include "logger.h"
24 #include "meshlink_internal.h"
25 #include "node.h"
26 #include "submesh.h"
27 #include "splay_tree.h"
28 #include "netutl.h"
29 #include "xalloc.h"
30
31 #include "devtools.h"
32
33 static void trybind_nop_probe(void) {
34         return;
35 }
36
37 static void keyrotate_nop_probe(int stage) {
38         (void)stage;
39         return;
40 }
41
42 static void inviter_commits_first_nop_probe(bool stage) {
43         (void)stage;
44         return;
45 }
46
47 void (*devtool_trybind_probe)(void) = trybind_nop_probe;
48 void (*devtool_keyrotate_probe)(int stage) = keyrotate_nop_probe;
49 void (*devtool_set_inviter_commits_first)(bool inviter_commited_first) = inviter_commits_first_nop_probe;
50
51 /* Return an array of edges in the current network graph.
52  * Data captures the current state and will not be updated.
53  * Caller must deallocate data when done.
54  */
55 devtool_edge_t *devtool_get_all_edges(meshlink_handle_t *mesh, devtool_edge_t *edges, size_t *nmemb) {
56         if(!mesh || !nmemb || (*nmemb && !edges)) {
57                 meshlink_errno = MESHLINK_EINVAL;
58                 return NULL;
59         }
60
61         pthread_mutex_lock(&mesh->mutex);
62
63         devtool_edge_t *result = NULL;
64         unsigned int result_size = 0;
65
66         result_size = mesh->edges->count / 2;
67
68         // if result is smaller than edges, we have to dealloc all the excess devtool_edge_t
69         if((size_t)result_size > *nmemb) {
70                 result = xrealloc(edges, result_size * sizeof(*result));
71         } else {
72                 result = edges;
73         }
74
75         if(result) {
76                 devtool_edge_t *p = result;
77                 unsigned int n = 0;
78
79                 for splay_each(edge_t, e, mesh->edges) {
80                         // skip edges that do not represent a two-directional connection
81                         if(!e->reverse || e->reverse->to != e->from) {
82                                 continue;
83                         }
84
85                         // don't count edges twice
86                         if(e->to < e->from) {
87                                 continue;
88                         }
89
90                         assert(n < result_size);
91
92                         p->from = (meshlink_node_t *)e->from;
93                         p->to = (meshlink_node_t *)e->to;
94                         p->address = e->address.storage;
95                         p->weight = e->weight;
96
97                         n++;
98                         p++;
99                 }
100
101                 // shrink result to the actual amount of memory used
102                 result = xrealloc(result, n * sizeof(*result));
103                 *nmemb = n;
104         } else {
105                 *nmemb = 0;
106                 meshlink_errno = MESHLINK_ENOMEM;
107         }
108
109         pthread_mutex_unlock(&mesh->mutex);
110
111         return result;
112 }
113
114 static bool fstrwrite(const char *str, FILE *stream) {
115         assert(stream);
116
117         size_t len = strlen(str);
118
119         if(fwrite((void *)str, 1, len, stream) != len) {
120                 return false;
121         }
122
123         return true;
124 }
125
126 bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE *stream) {
127         assert(stream);
128
129         bool result = true;
130
131         pthread_mutex_lock(&mesh->mutex);
132
133         // export edges and nodes
134         size_t node_count = 0;
135         size_t edge_count = 0;
136
137         meshlink_node_t **nodes = meshlink_get_all_nodes(mesh, NULL, &node_count);
138         devtool_edge_t *edges = devtool_get_all_edges(mesh, NULL, &edge_count);
139
140         if((!nodes && node_count != 0) || (!edges && edge_count != 0)) {
141                 goto fail;
142         }
143
144         // export begin
145         if(!fstrwrite("{\n", stream)) {
146                 goto fail;
147         }
148
149         // export nodes
150         if(!fstrwrite("\t\"nodes\": {\n", stream)) {
151                 goto fail;
152         }
153
154         char buf[16];
155
156         for(size_t i = 0; i < node_count; ++i) {
157                 if(!fstrwrite("\t\t\"", stream) || !fstrwrite(((node_t *)nodes[i])->name, stream) || !fstrwrite("\": {\n", stream)) {
158                         goto fail;
159                 }
160
161                 if(!fstrwrite("\t\t\t\"name\": \"", stream) || !fstrwrite(((node_t *)nodes[i])->name, stream) || !fstrwrite("\",\n", stream)) {
162                         goto fail;
163                 }
164
165                 snprintf(buf, sizeof(buf), "%d", ((node_t *)nodes[i])->devclass);
166
167                 if(!fstrwrite("\t\t\t\"devclass\": ", stream) || !fstrwrite(buf, stream) || !fstrwrite("\n", stream)) {
168                         goto fail;
169                 }
170
171                 if(!fstrwrite((i + 1) != node_count ? "\t\t},\n" : "\t\t}\n", stream)) {
172                         goto fail;
173                 }
174         }
175
176         if(!fstrwrite("\t},\n", stream)) {
177                 goto fail;
178         }
179
180         // export edges
181
182         if(!fstrwrite("\t\"edges\": {\n", stream)) {
183                 goto fail;
184         }
185
186         for(size_t i = 0; i < edge_count; ++i) {
187                 if(!fstrwrite("\t\t\"", stream) || !fstrwrite(edges[i].from->name, stream) || !fstrwrite("_to_", stream) || !fstrwrite(edges[i].to->name, stream) || !fstrwrite("\": {\n", stream)) {
188                         goto fail;
189                 }
190
191                 if(!fstrwrite("\t\t\t\"from\": \"", stream) || !fstrwrite(edges[i].from->name, stream) || !fstrwrite("\",\n", stream)) {
192                         goto fail;
193                 }
194
195                 if(!fstrwrite("\t\t\t\"to\": \"", stream) || !fstrwrite(edges[i].to->name, stream) || !fstrwrite("\",\n", stream)) {
196                         goto fail;
197                 }
198
199                 char *host = NULL, *port = NULL, *address = NULL;
200                 sockaddr2str((const sockaddr_t *)&edges[i].address, &host, &port);
201
202                 if(host && port) {
203                         xasprintf(&address, "{ \"host\": \"%s\", \"port\": %s }", host, port);
204                 }
205
206                 free(host);
207                 free(port);
208
209                 if(!fstrwrite("\t\t\t\"address\": ", stream) || !fstrwrite(address ? address : "null", stream) || !fstrwrite(",\n", stream)) {
210                         free(address);
211                         goto fail;
212                 }
213
214                 free(address);
215
216                 snprintf(buf, sizeof(buf), "%d", edges[i].weight);
217
218                 if(!fstrwrite("\t\t\t\"weight\": ", stream) || !fstrwrite(buf, stream) || !fstrwrite("\n", stream)) {
219                         goto fail;
220                 }
221
222                 if(!fstrwrite((i + 1) != edge_count ? "\t\t},\n" : "\t\t}\n", stream)) {
223                         goto fail;
224                 }
225         }
226
227         if(!fstrwrite("\t}\n", stream)) {
228                 goto fail;
229         }
230
231         // DONE!
232
233         if(!fstrwrite("}", stream)) {
234                 goto fail;
235         }
236
237         goto done;
238
239 fail:
240         result = false;
241
242 done:
243         free(nodes);
244         free(edges);
245
246         pthread_mutex_unlock(&mesh->mutex);
247
248         return result;
249 }
250
251 void devtool_get_node_status(meshlink_handle_t *mesh, meshlink_node_t *node, devtool_node_status_t *status) {
252         if(!mesh || !node || !status) {
253                 meshlink_errno = MESHLINK_EINVAL;
254                 return;
255         }
256
257         node_t *internal = (node_t *)node;
258
259         pthread_mutex_lock(&mesh->mutex);
260
261         memcpy(&status->status, &internal->status, sizeof status->status);
262         memcpy(&status->address, &internal->address, sizeof status->address);
263         status->mtu = internal->mtu;
264         status->minmtu = internal->minmtu;
265         status->maxmtu = internal->maxmtu;
266         status->mtuprobes = internal->mtuprobes;
267         status->in_packets = internal->in_packets;
268         status->in_bytes = internal->in_bytes;
269         status->out_packets = internal->out_packets;
270         status->out_bytes = internal->out_bytes;
271
272         // Derive UDP connection status
273         if(internal == mesh->self) {
274                 status->udp_status = DEVTOOL_UDP_WORKING;
275         } else if(!internal->status.reachable) {
276                 status->udp_status = DEVTOOL_UDP_IMPOSSIBLE;
277         } else if(!internal->status.validkey) {
278                 status->udp_status = DEVTOOL_UDP_UNKNOWN;
279         } else if(internal->status.udp_confirmed) {
280                 status->udp_status = DEVTOOL_UDP_WORKING;
281         } else if(internal->mtuprobes > 30) {
282                 status->udp_status = DEVTOOL_UDP_FAILED;
283         } else if(internal->mtuprobes > 0) {
284                 status->udp_status = DEVTOOL_UDP_TRYING;
285         } else {
286                 status->udp_status = DEVTOOL_UDP_UNKNOWN;
287         }
288
289         pthread_mutex_unlock(&mesh->mutex);
290 }
291
292 meshlink_submesh_t **devtool_get_all_submeshes(meshlink_handle_t *mesh, meshlink_submesh_t **submeshes, size_t *nmemb) {
293         if(!mesh || !nmemb || (*nmemb && !submeshes)) {
294                 meshlink_errno = MESHLINK_EINVAL;
295                 return NULL;
296         }
297
298         meshlink_submesh_t **result;
299
300         //lock mesh->nodes
301         pthread_mutex_lock(&mesh->mutex);
302
303         *nmemb = mesh->submeshes->count;
304         result = realloc(submeshes, *nmemb * sizeof(*submeshes));
305
306         if(result) {
307                 meshlink_submesh_t **p = result;
308
309                 for list_each(submesh_t, s, mesh->submeshes) {
310                         *p++ = (meshlink_submesh_t *)s;
311                 }
312         } else {
313                 *nmemb = 0;
314                 free(submeshes);
315                 meshlink_errno = MESHLINK_ENOMEM;
316         }
317
318         pthread_mutex_unlock(&mesh->mutex);
319
320         return result;
321 }
322
323 meshlink_handle_t *devtool_open_in_netns(const char *confbase, const char *name, const char *appname, dev_class_t devclass, int netns) {
324         meshlink_open_params_t *params = meshlink_open_params_init(confbase, name, appname, devclass);
325         params->netns = dup(netns);
326         meshlink_handle_t *handle;
327
328         if(params->netns == -1) {
329                 handle = NULL;
330                 meshlink_errno = MESHLINK_EINVAL;
331         } else {
332                 handle = meshlink_open_ex(params);
333         }
334
335         meshlink_open_params_free(params);
336
337         return handle;
338 }