]> git.meshlink.io Git - meshlink/blob - src/devtools.c
Add devtool_get_all_submeshes().
[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 /* Return an array of edges in the current network graph.
34  * Data captures the current state and will not be updated.
35  * Caller must deallocate data when done.
36  */
37 devtool_edge_t *devtool_get_all_edges(meshlink_handle_t *mesh, devtool_edge_t *edges, size_t *nmemb) {
38         if(!mesh || !nmemb || (*nmemb && !edges)) {
39                 meshlink_errno = MESHLINK_EINVAL;
40                 return NULL;
41         }
42
43         pthread_mutex_lock(&(mesh->mesh_mutex));
44
45         devtool_edge_t *result = NULL;
46         unsigned int result_size = 0;
47
48         result_size = mesh->edges->count / 2;
49
50         // if result is smaller than edges, we have to dealloc all the excess devtool_edge_t
51         if((size_t)result_size > *nmemb) {
52                 result = realloc(edges, result_size * sizeof(*result));
53         } else {
54                 result = edges;
55         }
56
57         if(result) {
58                 devtool_edge_t *p = result;
59                 unsigned int n = 0;
60
61                 for splay_each(edge_t, e, mesh->edges) {
62                         // skip edges that do not represent a two-directional connection
63                         if((!e->reverse) || (e->reverse->to != e->from)) {
64                                 continue;
65                         }
66
67                         // don't count edges twice
68                         if(e->to < e->from) {
69                                 continue;
70                         }
71
72                         assert(n < result_size);
73
74                         p->from = (meshlink_node_t *)e->from;
75                         p->to = (meshlink_node_t *)e->to;
76                         p->address = e->address.storage;
77                         p->options = e->options;
78                         p->weight = e->weight;
79
80                         n++;
81                         p++;
82                 }
83
84                 // shrink result to the actual amount of memory used
85                 result = realloc(result, n * sizeof(*result));
86                 *nmemb = n;
87         } else {
88                 *nmemb = 0;
89                 meshlink_errno = MESHLINK_ENOMEM;
90         }
91
92         pthread_mutex_unlock(&(mesh->mesh_mutex));
93
94         return result;
95 }
96
97 static bool fstrwrite(const char *str, FILE *stream) {
98         size_t len = strlen(str);
99
100         if(fwrite((void *)str, 1, len, stream) != len) {
101                 return false;
102         }
103
104         return true;
105 }
106
107 static const char *__itoa(int value) {
108         static char buffer[sizeof(int) * 8 + 1];        // not thread safe
109
110         if(snprintf(buffer, sizeof(buffer), "%d", value) == -1) {
111                 return "";
112         }
113
114         return buffer;
115 }
116
117 bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE *stream) {
118         bool result = true;
119
120         pthread_mutex_lock(&(mesh->mesh_mutex));
121
122         // export edges and nodes
123         size_t node_count = 0;
124         size_t edge_count = 0;
125
126         meshlink_node_t **nodes = meshlink_get_all_nodes(mesh, NULL, &node_count);
127         devtool_edge_t *edges = devtool_get_all_edges(mesh, NULL, &edge_count);
128
129         if((!nodes && node_count != 0) || (!edges && edge_count != 0)) {
130                 goto fail;
131         }
132
133         // export begin
134         if(!fstrwrite("{\n", stream)) {
135                 goto fail;
136         }
137
138         // export nodes
139         if(!fstrwrite("\t\"nodes\": {\n", stream)) {
140                 goto fail;
141         }
142
143         for(size_t i = 0; i < node_count; ++i) {
144                 if(!fstrwrite("\t\t\"", stream) || !fstrwrite(((node_t *)nodes[i])->name, stream) || !fstrwrite("\": {\n", stream)) {
145                         goto fail;
146                 }
147
148                 if(!fstrwrite("\t\t\t\"name\": \"", stream) || !fstrwrite(((node_t *)nodes[i])->name, stream) || !fstrwrite("\",\n", stream)) {
149                         goto fail;
150                 }
151
152                 if(!fstrwrite("\t\t\t\"options\": ", stream) || !fstrwrite(__itoa(((node_t *)nodes[i])->options), stream) || !fstrwrite(",\n", stream)) {
153                         goto fail;
154                 }
155
156                 if(!fstrwrite("\t\t\t\"devclass\": ", stream) || !fstrwrite(__itoa(((node_t *)nodes[i])->devclass), stream) || !fstrwrite("\n", stream)) {
157                         goto fail;
158                 }
159
160                 if(!fstrwrite((i + 1) != node_count ? "\t\t},\n" : "\t\t}\n", stream)) {
161                         goto fail;
162                 }
163         }
164
165         if(!fstrwrite("\t},\n", stream)) {
166                 goto fail;
167         }
168
169         // export edges
170
171         if(!fstrwrite("\t\"edges\": {\n", stream)) {
172                 goto fail;
173         }
174
175         for(size_t i = 0; i < edge_count; ++i) {
176                 if(!fstrwrite("\t\t\"", stream) || !fstrwrite(edges[i].from->name, stream) || !fstrwrite("_to_", stream) || !fstrwrite(edges[i].to->name, stream) || !fstrwrite("\": {\n", stream)) {
177                         goto fail;
178                 }
179
180                 if(!fstrwrite("\t\t\t\"from\": \"", stream) || !fstrwrite(edges[i].from->name, stream) || !fstrwrite("\",\n", stream)) {
181                         goto fail;
182                 }
183
184                 if(!fstrwrite("\t\t\t\"to\": \"", stream) || !fstrwrite(edges[i].to->name, stream) || !fstrwrite("\",\n", stream)) {
185                         goto fail;
186                 }
187
188                 char *host = NULL, *port = NULL, *address = NULL;
189                 sockaddr2str((const sockaddr_t *)&edges[i].address, &host, &port);
190
191                 if(host && port) {
192                         xasprintf(&address, "{ \"host\": \"%s\", \"port\": %s }", host, port);
193                 }
194
195                 free(host);
196                 free(port);
197
198                 if(!fstrwrite("\t\t\t\"address\": ", stream) || !fstrwrite(address ? address : "null", stream) || !fstrwrite(",\n", stream)) {
199                         free(address);
200                         goto fail;
201                 }
202
203                 free(address);
204
205                 if(!fstrwrite("\t\t\t\"options\": ", stream) || !fstrwrite(__itoa(edges[i].options), stream) || !fstrwrite(",\n", stream)) {
206                         goto fail;
207                 }
208
209                 if(!fstrwrite("\t\t\t\"weight\": ", stream) || !fstrwrite(__itoa(edges[i].weight), stream) || !fstrwrite("\n", stream)) {
210                         goto fail;
211                 }
212
213                 if(!fstrwrite((i + 1) != edge_count ? "\t\t},\n" : "\t\t}\n", stream)) {
214                         goto fail;
215                 }
216         }
217
218         if(!fstrwrite("\t}\n", stream)) {
219                 goto fail;
220         }
221
222         // DONE!
223
224         if(!fstrwrite("}", stream)) {
225                 goto fail;
226         }
227
228         goto done;
229
230 fail:
231         result = false;
232
233 done:
234         free(nodes);
235         free(edges);
236
237         pthread_mutex_unlock(&(mesh->mesh_mutex));
238
239         return result;
240 }
241
242 void devtool_get_node_status(meshlink_handle_t *mesh, meshlink_node_t *node, devtool_node_status_t *status) {
243         if(!mesh || !node || !status) {
244                 meshlink_errno = MESHLINK_EINVAL;
245                 return;
246         }
247
248         node_t *internal = (node_t *)node;
249
250         pthread_mutex_lock(&mesh->mesh_mutex);
251
252         status->options = internal->options;
253         memcpy(&status->status, &internal->status, sizeof status->status);
254         memcpy(&status->address, &internal->address, sizeof status->address);
255         status->mtu = internal->mtu;
256         status->minmtu = internal->minmtu;
257         status->maxmtu = internal->maxmtu;
258         status->mtuprobes = internal->mtuprobes;
259         status->in_packets = internal->in_packets;
260         status->in_bytes = internal->in_bytes;
261         status->out_packets = internal->out_packets;
262         status->out_bytes = internal->out_bytes;
263
264         // Derive UDP connection status
265         if(internal == mesh->self) {
266                 status->udp_status = DEVTOOL_UDP_WORKING;
267         } else if(!internal->status.reachable) {
268                 status->udp_status = DEVTOOL_UDP_IMPOSSIBLE;
269         } else if(!internal->status.validkey) {
270                 status->udp_status = DEVTOOL_UDP_UNKNOWN;
271         } else if(internal->status.udp_confirmed) {
272                 status->udp_status = DEVTOOL_UDP_WORKING;
273         } else if(internal->mtuprobes > 30) {
274                 status->udp_status = DEVTOOL_UDP_FAILED;
275         } else if(internal->mtuprobes > 0) {
276                 status->udp_status = DEVTOOL_UDP_TRYING;
277         } else {
278                 status->udp_status = DEVTOOL_UDP_UNKNOWN;
279         }
280
281         pthread_mutex_unlock(&mesh->mesh_mutex);
282 }
283
284 meshlink_submesh_t **devtool_get_all_submeshes(meshlink_handle_t *mesh, meshlink_submesh_t **submeshes, size_t *nmemb) {
285         if(!mesh || !nmemb || (*nmemb && !submeshes)) {
286                 meshlink_errno = MESHLINK_EINVAL;
287                 return NULL;
288         }
289
290         meshlink_submesh_t **result;
291
292         //lock mesh->nodes
293         pthread_mutex_lock(&(mesh->mesh_mutex));
294
295         *nmemb = mesh->submeshes->count;
296         result = realloc(submeshes, *nmemb * sizeof(*submeshes));
297
298         if(result) {
299                 meshlink_submesh_t **p = result;
300
301                 for list_each(submesh_t, s, mesh->submeshes) {
302                         *p++ = (meshlink_submesh_t *)s;
303                 }
304         } else {
305                 *nmemb = 0;
306                 free(submeshes);
307                 meshlink_errno = MESHLINK_ENOMEM;
308         }
309
310         pthread_mutex_unlock(&(mesh->mesh_mutex));
311
312         return result;
313 }
314 meshlink_handle_t *devtool_open_in_netns(const char *confbase, const char *name, const char *appname, dev_class_t devclass, int netns) {
315         meshlink_open_params_t *params = meshlink_open_params_init(confbase, name, appname, devclass);
316         params->netns = dup(netns);
317         meshlink_handle_t *handle;
318
319         if(params->netns == -1) {
320                 handle = NULL;
321                 meshlink_errno = MESHLINK_EINVAL;
322         } else {
323                 handle = meshlink_open_ex(params);
324         }
325
326         meshlink_open_params_free(params);
327
328         return handle;
329 }