Check that you are not present in trail twice
[oweals/gnunet.git] / src / transport / gnunet-service-transport_plugins.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/gnunet-service-transport_plugins.c
23  * @brief plugin management
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-transport.h"
28 #include "gnunet-service-transport_hello.h"
29 #include "gnunet-service-transport_plugins.h"
30
31 /**
32  * Entry in doubly-linked list of all of our plugins.
33  */
34 struct TransportPlugin
35 {
36   /**
37    * This is a doubly-linked list.
38    */
39   struct TransportPlugin *next;
40
41   /**
42    * This is a doubly-linked list.
43    */
44   struct TransportPlugin *prev;
45
46   /**
47    * API of the transport as returned by the plugin's
48    * initialization function.
49    */
50   struct GNUNET_TRANSPORT_PluginFunctions *api;
51
52   /**
53    * Short name for the plugin (i.e. "tcp").
54    */
55   char *short_name;
56
57   /**
58    * Name of the library (i.e. "gnunet_plugin_transport_tcp").
59    */
60   char *lib_name;
61
62   /**
63    * Environment this transport service is using
64    * for this plugin.
65    */
66   struct GNUNET_TRANSPORT_PluginEnvironment env;
67
68 };
69
70 /**
71  * Head of DLL of all loaded plugins.
72  */
73 static struct TransportPlugin *plugins_head;
74
75 /**
76  * Head of DLL of all loaded plugins.
77  */
78 static struct TransportPlugin *plugins_tail;
79
80
81
82 /**
83  * Load and initialize all plugins.  The respective functions will be
84  * invoked by the plugins when the respective events happen.  The
85  * closure will be set to a 'const char*' containing the name of the
86  * plugin that caused the call.
87  *
88  * @param recv_cb function to call when data is received
89  * @param register_quota_cb function to call to register a quota callback
90  * @param unregister_quota_cb function to call to unregister a quota callback
91  * @param address_cb function to call when our public addresses changed
92  * @param session_start_cb function to call when a session was created
93  * @param session_end_cb function to call when a session was terminated
94  * @param address_type_cb function to call when a address type is requested
95  * @param metric_update_cb function to call when address metrics change
96  */
97 void
98 GST_plugins_load (GNUNET_TRANSPORT_PluginReceiveCallback recv_cb,
99                   GNUNET_TRANSPORT_RegisterQuotaNotification register_quota_cb,
100                   GNUNET_TRANSPORT_UnregisterQuotaNotification unregister_quota_cb,
101                   GNUNET_TRANSPORT_AddressNotification address_cb,
102                   GNUNET_TRANSPORT_SessionStart session_start_cb,
103                   GNUNET_TRANSPORT_SessionEnd session_end_cb,
104                   GNUNET_TRANSPORT_AddressToType address_type_cb,
105                   GNUNET_TRANSPORT_UpdateAddressMetrics metric_update_cb)
106 {
107   struct TransportPlugin *plug;
108   struct TransportPlugin *next;
109   unsigned long long tneigh;
110   char *libname;
111   char *plugs;
112   char *pos;
113   int fail;
114
115   if (GNUNET_OK !=
116       GNUNET_CONFIGURATION_get_value_number (GST_cfg, "TRANSPORT",
117                                              "NEIGHBOUR_LIMIT", &tneigh))
118   {
119     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
120                 _("Transport service is lacking NEIGHBOUR_LIMIT option.\n"));
121     return;
122   }
123   if (GNUNET_OK !=
124       GNUNET_CONFIGURATION_get_value_string (GST_cfg, "TRANSPORT", "PLUGINS",
125                                              &plugs))
126     return;
127   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
128               _("Starting transport plugins `%s'\n"),
129               plugs);
130   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
131   {
132     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
133                 _("Loading `%s' transport plugin\n"),
134                 pos);
135     GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", pos);
136     plug = GNUNET_new (struct TransportPlugin);
137     plug->short_name = GNUNET_strdup (pos);
138     plug->lib_name = libname;
139     plug->env.cfg = GST_cfg;
140     plug->env.my_identity = &GST_my_identity;
141     plug->env.get_our_hello = &GST_hello_get;
142     plug->env.cls = plug->short_name;
143     plug->env.receive = recv_cb;
144     plug->env.notify_address = address_cb;
145     plug->env.session_start = session_start_cb;
146     plug->env.session_end = session_end_cb;
147     plug->env.get_address_type = address_type_cb;
148     plug->env.update_address_metrics = metric_update_cb;
149     plug->env.register_quota_notification = register_quota_cb;
150     plug->env.unregister_quota_notification = unregister_quota_cb;
151     plug->env.max_connections = tneigh;
152     plug->env.stats = GST_stats;
153     GNUNET_CONTAINER_DLL_insert (plugins_head, plugins_tail, plug);
154   }
155   GNUNET_free (plugs);
156   next = plugins_head;
157   while (next != NULL)
158   {
159     plug = next;
160     next = plug->next;
161     plug->api = GNUNET_PLUGIN_load (plug->lib_name, &plug->env);
162     if (NULL == plug->api)
163     {
164       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
165                   _("Failed to load transport plugin for `%s'\n"),
166                   plug->lib_name);
167       GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
168       GNUNET_free (plug->short_name);
169       GNUNET_free (plug->lib_name);
170       GNUNET_free (plug);
171       continue;
172     }
173     fail = GNUNET_NO;
174     if (NULL == plug->api->address_pretty_printer)
175     {
176         fail = GNUNET_YES;
177       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
178                   _("Missing function `%s' in transport plugin for `%s'\n"),
179                   "address_pretty_printer",
180                   plug->lib_name);
181     }
182     if (NULL == plug->api->address_to_string)
183     {
184         fail = GNUNET_YES;
185       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
186                   _("Missing function `%s' in transport plugin for `%s'\n"),
187                   "address_to_string",
188                   plug->lib_name);
189     }
190     if (NULL == plug->api->string_to_address)
191     {
192         fail = GNUNET_YES;
193       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
194                   _("Missing function `%s' in transport plugin for `%s'\n"),
195                   "string_to_address",
196                   plug->lib_name);
197     }
198     if (NULL == plug->api->check_address)
199     {
200         fail = GNUNET_YES;
201       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
202                   _("Missing function `%s' in transport plugin for `%s'\n"),
203                   "check_address",
204                   plug->lib_name);
205     }
206     if (NULL == plug->api->get_session)
207     {
208         fail = GNUNET_YES;
209       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
210                   _("Missing function `%s' in transport plugin for `%s'\n"),
211                   "get_session",
212                   plug->lib_name);
213     }
214     if (NULL == plug->api->get_network)
215     {
216         fail = GNUNET_YES;
217       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
218                   _("Missing function `%s' in transport plugin for `%s'\n"),
219                   "get_network",
220                   plug->lib_name);
221     }
222     if (NULL == plug->api->send)
223     {
224         fail = GNUNET_YES;
225       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
226                   _("Missing function `%s' in transport plugin for `%s'\n"),
227                   "send",
228                   plug->lib_name);
229     }
230     if (NULL == plug->api->disconnect_peer)
231     {
232         fail = GNUNET_YES;
233       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
234                   _("Missing function `%s' in transport plugin for `%s'\n"),
235                   "disconnect_peer",
236                   plug->lib_name);
237     }
238     if (NULL == plug->api->disconnect_session)
239     {
240         fail = GNUNET_YES;
241       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
242                   _("Missing function `%s' in transport plugin for `%s'\n"),
243                   "disconnect_session",
244                   plug->lib_name);
245     }
246     if (NULL == plug->api->query_keepalive_factor)
247     {
248       fail = GNUNET_YES;
249       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250                   _("Missing function `%s' in transport plugin for `%s'\n"),
251                   "query_keepalive_factor",
252                   plug->lib_name);
253     }
254     if (NULL == plug->api->update_session_timeout)
255     {
256         fail = GNUNET_YES;
257       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
258                   _("Missing function `%s' in transport plugin for `%s'\n"),
259                   "update_session_timeout",
260                   plug->lib_name);
261     }
262     if (GNUNET_YES == fail)
263     {
264       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
265                   _("Did not load plugin `%s' due to missing functions\n"),
266                   plug->lib_name);
267       GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
268       GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
269       GNUNET_free (plug->short_name);
270       GNUNET_free (plug->lib_name);
271       GNUNET_free (plug);
272     }
273   }
274 }
275
276
277 /**
278  * Unload all plugins
279  */
280 void
281 GST_plugins_unload ()
282 {
283   struct TransportPlugin *plug;
284
285   while (NULL != (plug = plugins_head))
286   {
287     GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
288     GNUNET_free (plug->lib_name);
289     GNUNET_free (plug->short_name);
290     GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
291     GNUNET_free (plug);
292   }
293 }
294
295
296 /**
297  * Obtain the plugin API based on a plugin name.
298  *
299  * @param name name of the plugin
300  * @return the plugin's API, NULL if the plugin is not loaded
301  */
302 struct GNUNET_TRANSPORT_PluginFunctions *
303 GST_plugins_find (const char *name)
304 {
305   struct TransportPlugin *head = plugins_head;
306
307   while ((head != NULL) && (0 != strcmp (name, head->short_name)))
308     head = head->next;
309   if (NULL == head)
310     return NULL;
311   return head->api;
312 }
313
314
315 /**
316  * Obtain the plugin API based on a the stripped plugin name after the underscore.
317  *
318  * Example: GST_plugins_printer_find (http_client) will return all plugins
319  * starting with the prefix "http":
320  * http_client or server if loaded
321  *
322  * @param name name of the plugin
323  * @return the plugin's API, NULL if the plugin is not loaded
324  */
325 struct GNUNET_TRANSPORT_PluginFunctions *
326 GST_plugins_printer_find (const char *name)
327 {
328   struct TransportPlugin *head = plugins_head;
329
330   char *stripped = GNUNET_strdup (name);
331   char *sep = strchr (stripped, '_');
332   if (NULL != sep)
333     sep[0] = '\0';
334
335   while (head != NULL)
336   {
337     if (head->short_name == strstr (head->short_name, stripped))
338         break;
339     head = head->next;
340   }
341   GNUNET_free (stripped);
342   if (NULL == head)
343     return NULL;
344   return head->api;
345 }
346
347
348 /**
349  * Convert a given address to a human-readable format.  Note that the
350  * return value will be overwritten on the next call to this function.
351  *
352  * @param address the address to convert
353  * @return statically allocated (!) human-readable address
354  */
355 const char *
356 GST_plugins_a2s (const struct GNUNET_HELLO_Address *address)
357 {
358   struct GNUNET_TRANSPORT_PluginFunctions *api;
359   static char unable_to_show[1024];
360   static const char *s;
361
362   if (address == NULL)
363   {
364         GNUNET_break (0); /* a HELLO address cannot be NULL */
365     return "<invalid>";
366   }
367   if (0 == address->address_length)
368     return TRANSPORT_SESSION_INBOUND_STRING; /* Addresse with length 0 are inbound, address->address itself may be NULL */
369   api = GST_plugins_printer_find (address->transport_name);
370   if (NULL == api)
371     return "<plugin unknown>";
372   if (0 == address->address_length)
373   {
374     GNUNET_snprintf (unable_to_show, sizeof (unable_to_show),
375                      "<unable to stringify %u-byte long address of %s transport>",
376                      (unsigned int) address->address_length,
377                      address->transport_name);
378     return unable_to_show;
379   }
380   return (NULL != (s = api->address_to_string (NULL, address->address,
381                                  address->address_length)) ? s : "<invalid>");
382 }
383
384
385 /* end of file gnunet-service-transport_plugins.c */