5466d32b7d10b2f3bc908dae9a7c6e66b5cb7b7f
[oweals/gnunet.git] / src / transport / gnunet-service-transport_plugins.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2010-2014 GNUnet e.V.
4
5   GNUnet is free software: you can redistribute it and/or modify it
6   under the terms of the GNU General Public License as published
7   by the Free Software Foundation, either version 3 of the License,
8   or (at your option) any later version.
9
10   GNUnet is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Affero General Public License for more details.
14 */
15
16 /**
17  * @file transport/gnunet-service-transport_plugins.c
18  * @brief plugin management
19  * @author Christian Grothoff
20  */
21 #include "platform.h"
22 #include "gnunet-service-transport.h"
23 #include "gnunet-service-transport_hello.h"
24 #include "gnunet-service-transport_ats.h"
25 #include "gnunet-service-transport_plugins.h"
26
27 /**
28  * Entry in doubly-linked list of all of our plugins.
29  */
30 struct TransportPlugin
31 {
32   /**
33    * This is a doubly-linked list.
34    */
35   struct TransportPlugin *next;
36
37   /**
38    * This is a doubly-linked list.
39    */
40   struct TransportPlugin *prev;
41
42   /**
43    * API of the transport as returned by the plugin's
44    * initialization function.
45    */
46   struct GNUNET_TRANSPORT_PluginFunctions *api;
47
48   /**
49    * Short name for the plugin (i.e. "tcp").
50    */
51   char *short_name;
52
53   /**
54    * Name of the library (i.e. "gnunet_plugin_transport_tcp").
55    */
56   char *lib_name;
57
58   /**
59    * Environment this transport service is using
60    * for this plugin.
61    */
62   struct GNUNET_TRANSPORT_PluginEnvironment env;
63
64 };
65
66 /**
67  * Head of DLL of all loaded plugins.
68  */
69 static struct TransportPlugin *plugins_head;
70
71 /**
72  * Head of DLL of all loaded plugins.
73  */
74 static struct TransportPlugin *plugins_tail;
75
76
77 /**
78  * Function that will be called to update metrics for an address
79  *
80  * @param cls closure
81  * @param address address to update metrics for
82  * @param session the session
83  * @param distance new distance
84  */
85 static void
86 plugin_env_update_distance (void *cls,
87                             const struct GNUNET_HELLO_Address *address,
88                             uint32_t distance)
89 {
90   GST_ats_update_distance (address,
91                            distance);
92 }
93
94
95 /**
96  * Function that will be called to figure if an address is an loopback,
97  * LAN, WAN etc. address
98  *
99  * @param cls closure
100  * @param addr binary address
101  * @param addrlen length of the @a addr
102  * @return type of the network @a addr belongs to
103  */
104 static enum GNUNET_ATS_Network_Type
105 plugin_env_address_to_type (void *cls,
106                             const struct sockaddr *addr,
107                             size_t addrlen)
108 {
109   if (NULL == GST_is)
110   {
111     GNUNET_break(0);
112     return GNUNET_ATS_NET_UNSPECIFIED;
113   }
114   return GNUNET_ATS_scanner_address_get_type (GST_is,
115                                               addr,
116                                               addrlen);
117 }
118
119
120 /**
121  * Load and initialize all plugins.  The respective functions will be
122  * invoked by the plugins when the respective events happen.  The
123  * closure will be set to a 'const char*' containing the name of the
124  * plugin that caused the call.
125  *
126  * @param recv_cb function to call when data is received
127  * @param address_cb function to call when our public addresses changed
128  * @param session_start_cb function to call when a session was created
129  * @param session_end_cb function to call when a session was terminated
130  * @param address_type_cb function to call when a address type is requested
131  */
132 void
133 GST_plugins_load (GNUNET_TRANSPORT_PluginReceiveCallback recv_cb,
134                   GNUNET_TRANSPORT_AddressNotification address_cb,
135                   GNUNET_TRANSPORT_SessionStart session_start_cb,
136                   GNUNET_TRANSPORT_SessionEnd session_end_cb)
137 {
138   struct TransportPlugin *plug;
139   struct TransportPlugin *next;
140   unsigned long long tneigh;
141   char *libname;
142   char *plugs;
143   char *pos;
144   int fail;
145
146   if (GNUNET_OK !=
147       GNUNET_CONFIGURATION_get_value_number (GST_cfg,
148                                              "TRANSPORT",
149                                              "NEIGHBOUR_LIMIT",
150                                              &tneigh))
151   {
152     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
153                 _("Transport service is lacking NEIGHBOUR_LIMIT option.\n"));
154     return;
155   }
156   if (GNUNET_OK !=
157       GNUNET_CONFIGURATION_get_value_string (GST_cfg,
158                                              "TRANSPORT",
159                                              "PLUGINS",
160                                              &plugs))
161     return;
162   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
163               _("Starting transport plugins `%s'\n"),
164               plugs);
165   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
166   {
167     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
168                 _("Loading `%s' transport plugin\n"),
169                 pos);
170     GNUNET_asprintf (&libname,
171                      "libgnunet_plugin_transport_%s",
172                      pos);
173     plug = GNUNET_new (struct TransportPlugin);
174     plug->short_name = GNUNET_strdup (pos);
175     plug->lib_name = libname;
176     plug->env.cfg = GST_cfg;
177     plug->env.my_identity = &GST_my_identity;
178     plug->env.get_our_hello = &GST_hello_get;
179     plug->env.cls = plug->short_name;
180     plug->env.receive = recv_cb;
181     plug->env.notify_address = address_cb;
182     plug->env.session_start = session_start_cb;
183     plug->env.session_end = session_end_cb;
184     plug->env.get_address_type = &plugin_env_address_to_type;
185     plug->env.update_address_distance = &plugin_env_update_distance;
186     plug->env.max_connections = tneigh;
187     plug->env.stats = GST_stats;
188     GNUNET_CONTAINER_DLL_insert (plugins_head,
189                                  plugins_tail,
190                                  plug);
191   }
192   GNUNET_free (plugs);
193   next = plugins_head;
194   while (NULL != next)
195   {
196     plug = next;
197     next = plug->next;
198     plug->api = GNUNET_PLUGIN_load (plug->lib_name,
199                                     &plug->env);
200     if (NULL == plug->api)
201     {
202       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
203                   _("Failed to load transport plugin for `%s'\n"),
204                   plug->lib_name);
205       GNUNET_CONTAINER_DLL_remove (plugins_head,
206                                    plugins_tail,
207                                    plug);
208       GNUNET_free (plug->short_name);
209       GNUNET_free (plug->lib_name);
210       GNUNET_free (plug);
211       continue;
212     }
213     fail = GNUNET_NO;
214     if (NULL == plug->api->address_pretty_printer)
215     {
216         fail = GNUNET_YES;
217       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
218                   _("Missing function `%s' in transport plugin for `%s'\n"),
219                   "address_pretty_printer",
220                   plug->lib_name);
221     }
222     if (NULL == plug->api->address_to_string)
223     {
224         fail = GNUNET_YES;
225       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
226                   _("Missing function `%s' in transport plugin for `%s'\n"),
227                   "address_to_string",
228                   plug->lib_name);
229     }
230     if (NULL == plug->api->string_to_address)
231     {
232         fail = GNUNET_YES;
233       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
234                   _("Missing function `%s' in transport plugin for `%s'\n"),
235                   "string_to_address",
236                   plug->lib_name);
237     }
238     if (NULL == plug->api->check_address)
239     {
240       fail = GNUNET_YES;
241       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
242                   _("Missing function `%s' in transport plugin for `%s'\n"),
243                   "check_address",
244                   plug->lib_name);
245     }
246     if (NULL == plug->api->get_session)
247     {
248       fail = GNUNET_YES;
249       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250                   _("Missing function `%s' in transport plugin for `%s'\n"),
251                   "get_session",
252                   plug->lib_name);
253     }
254     if (NULL == plug->api->get_network)
255     {
256       fail = GNUNET_YES;
257       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
258                   _("Missing function `%s' in transport plugin for `%s'\n"),
259                   "get_network",
260                   plug->lib_name);
261     }
262     if (NULL == plug->api->send)
263     {
264       fail = GNUNET_YES;
265       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
266                   _("Missing function `%s' in transport plugin for `%s'\n"),
267                   "send",
268                   plug->lib_name);
269     }
270     if (NULL == plug->api->disconnect_peer)
271     {
272         fail = GNUNET_YES;
273       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
274                   _("Missing function `%s' in transport plugin for `%s'\n"),
275                   "disconnect_peer",
276                   plug->lib_name);
277     }
278     if (NULL == plug->api->disconnect_session)
279     {
280         fail = GNUNET_YES;
281       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
282                   _("Missing function `%s' in transport plugin for `%s'\n"),
283                   "disconnect_session",
284                   plug->lib_name);
285     }
286     if (NULL == plug->api->query_keepalive_factor)
287     {
288       fail = GNUNET_YES;
289       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
290                   _("Missing function `%s' in transport plugin for `%s'\n"),
291                   "query_keepalive_factor",
292                   plug->lib_name);
293     }
294     if (NULL == plug->api->update_session_timeout)
295     {
296         fail = GNUNET_YES;
297       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
298                   _("Missing function `%s' in transport plugin for `%s'\n"),
299                   "update_session_timeout",
300                   plug->lib_name);
301     }
302     if (GNUNET_YES == fail)
303     {
304       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
305                   _("Did not load plugin `%s' due to missing functions\n"),
306                   plug->lib_name);
307       GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
308       GNUNET_CONTAINER_DLL_remove (plugins_head,
309                                    plugins_tail,
310                                    plug);
311       GNUNET_free (plug->short_name);
312       GNUNET_free (plug->lib_name);
313       GNUNET_free (plug);
314     }
315   }
316 }
317
318
319 /**
320  * Unload all plugins
321  */
322 void
323 GST_plugins_unload ()
324 {
325   struct TransportPlugin *plug;
326
327   while (NULL != (plug = plugins_head))
328   {
329     GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
330     GNUNET_free (plug->lib_name);
331     GNUNET_free (plug->short_name);
332     GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
333     GNUNET_free (plug);
334   }
335 }
336
337
338 /**
339  * Obtain the plugin API based on a plugin name.
340  *
341  * @param name name of the plugin
342  * @return the plugin's API, NULL if the plugin is not loaded
343  */
344 struct GNUNET_TRANSPORT_PluginFunctions *
345 GST_plugins_find (const char *name)
346 {
347   struct TransportPlugin *pos;
348
349   for (pos = plugins_head; NULL != pos; pos = pos->next)
350     if (0 == strcmp (name, pos->short_name))
351       break;
352   if (NULL == pos)
353     return NULL;
354   return pos->api;
355 }
356
357
358 /**
359  * Obtain the plugin API based on a the stripped plugin name after the underscore.
360  *
361  * Example: GST_plugins_printer_find (http_client) will return all plugins
362  * starting with the prefix "http":
363  * http_client or server if loaded
364  *
365  * @param name name of the plugin
366  * @return the plugin's API, NULL if the plugin is not loaded
367  */
368 struct GNUNET_TRANSPORT_PluginFunctions *
369 GST_plugins_printer_find (const char *name)
370 {
371   struct TransportPlugin *pos;
372   char *stripped = GNUNET_strdup (name);
373   char *sep = strchr (stripped, '_');
374
375   if (NULL != sep)
376     sep[0] = '\0';
377   for (pos = plugins_head; NULL != pos; pos = pos->next)
378     if (pos->short_name == strstr (pos->short_name, stripped))
379         break;
380   GNUNET_free (stripped);
381   if (NULL == pos)
382     return NULL;
383   return pos->api;
384 }
385
386
387 /**
388  * Convert a given address to a human-readable format.  Note that the
389  * return value will be overwritten on the next call to this function.
390  *
391  * @param address the address to convert
392  * @return statically allocated (!) human-readable address
393  */
394 const char *
395 GST_plugins_a2s (const struct GNUNET_HELLO_Address *address)
396 {
397   struct GNUNET_TRANSPORT_PluginFunctions *api;
398   static char unable_to_show[1024];
399   static const char *s;
400
401   if (NULL == address)
402     return "<NULL>";
403   if (0 == address->address_length)
404     return TRANSPORT_SESSION_INBOUND_STRING; /* Addresse with length 0 are inbound, address->address itself may be NULL */
405   api = GST_plugins_printer_find (address->transport_name);
406   if (NULL == api)
407   {
408     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
409                 "Failed to find transport plugin `%s'\n",
410                 address->transport_name);
411     return "<plugin unknown>";
412   }
413   if (0 == address->address_length)
414   {
415     GNUNET_snprintf (unable_to_show,
416                      sizeof (unable_to_show),
417                      "<unable to stringify %u-byte long address of %s transport>",
418                      (unsigned int) address->address_length,
419                      address->transport_name);
420     return unable_to_show;
421   }
422   return (NULL != (s = api->address_to_string (NULL,
423                                                address->address,
424                                                address->address_length))
425           ? s
426           : "<invalid>");
427 }
428
429
430 /**
431  * Register callback with all plugins to monitor their status.
432  *
433  * @param cb callback to register, NULL to unsubscribe
434  * @param cb_cls closure for @a cb
435  */
436 void
437 GST_plugins_monitor_subscribe (GNUNET_TRANSPORT_SessionInfoCallback cb,
438                                void *cb_cls)
439 {
440   struct TransportPlugin *pos;
441
442   for (pos = plugins_head; NULL != pos; pos = pos->next)
443     if (NULL == pos->api->setup_monitor)
444       GNUNET_break (0);
445     else
446       pos->api->setup_monitor (pos->api->cls,
447                                cb,
448                                cb_cls);
449 }
450
451
452 /* end of file gnunet-service-transport_plugins.c */