- client-side implementation of peer queries
[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 address_cb function to call when our public addresses changed
90  * @param session_start_cb function to call when a session was created
91  * @param session_end_cb function to call when a session was terminated
92  * @param address_type_cb function to call when a address type is requested
93  * @param metric_update_cb function to call when address metrics change
94  */
95 void
96 GST_plugins_load (GNUNET_TRANSPORT_PluginReceiveCallback recv_cb,
97                   GNUNET_TRANSPORT_AddressNotification address_cb,
98                   GNUNET_TRANSPORT_SessionStart session_start_cb,
99                   GNUNET_TRANSPORT_SessionEnd session_end_cb,
100                   GNUNET_TRANSPORT_AddressToType address_type_cb,
101                   GNUNET_TRANSPORT_UpdateAddressMetrics metric_update_cb)
102 {
103   struct TransportPlugin *plug;
104   struct TransportPlugin *next;
105   unsigned long long tneigh;
106   char *libname;
107   char *plugs;
108   char *pos;
109   int fail;
110
111   if (GNUNET_OK !=
112       GNUNET_CONFIGURATION_get_value_number (GST_cfg, "TRANSPORT",
113                                              "NEIGHBOUR_LIMIT", &tneigh))
114   {
115     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
116                 _("Transport service is lacking NEIGHBOUR_LIMIT option.\n"));
117     return;
118   }
119   if (GNUNET_OK !=
120       GNUNET_CONFIGURATION_get_value_string (GST_cfg, "TRANSPORT", "PLUGINS",
121                                              &plugs))
122     return;
123   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
124               _("Starting transport plugins `%s'\n"),
125               plugs);
126   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
127   {
128     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
129                 _("Loading `%s' transport plugin\n"),
130                 pos);
131     GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", pos);
132     plug = GNUNET_new (struct TransportPlugin);
133     plug->short_name = GNUNET_strdup (pos);
134     plug->lib_name = libname;
135     plug->env.cfg = GST_cfg;
136     plug->env.my_identity = &GST_my_identity;
137     plug->env.get_our_hello = &GST_hello_get;
138     plug->env.cls = plug->short_name;
139     plug->env.receive = recv_cb;
140     plug->env.notify_address = address_cb;
141     plug->env.session_start = session_start_cb;
142     plug->env.session_end = session_end_cb;
143     plug->env.get_address_type = address_type_cb;
144     plug->env.update_address_metrics = metric_update_cb;
145     plug->env.max_connections = tneigh;
146     plug->env.stats = GST_stats;
147     GNUNET_CONTAINER_DLL_insert (plugins_head, plugins_tail, plug);
148   }
149   GNUNET_free (plugs);
150   next = plugins_head;
151   while (next != NULL)
152   {
153     plug = next;
154     next = plug->next;
155     plug->api = GNUNET_PLUGIN_load (plug->lib_name, &plug->env);
156     if (NULL == plug->api)
157     {
158       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
159                   _("Failed to load transport plugin for `%s'\n"),
160                   plug->lib_name);
161       GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
162       GNUNET_free (plug->short_name);
163       GNUNET_free (plug->lib_name);
164       GNUNET_free (plug);
165       continue;
166     }
167     fail = GNUNET_NO;
168     if (NULL == plug->api->address_pretty_printer)
169     {
170         fail = GNUNET_YES;
171       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
172                   _("Missing function `%s' in transport plugin for `%s'\n"),
173                   "address_pretty_printer",
174                   plug->lib_name);
175     }
176     if (NULL == plug->api->address_to_string)
177     {
178         fail = GNUNET_YES;
179       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
180                   _("Missing function `%s' in transport plugin for `%s'\n"),
181                   "address_to_string",
182                   plug->lib_name);
183     }
184     if (NULL == plug->api->string_to_address)
185     {
186         fail = GNUNET_YES;
187       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
188                   _("Missing function `%s' in transport plugin for `%s'\n"),
189                   "string_to_address",
190                   plug->lib_name);
191     }
192     if (NULL == plug->api->check_address)
193     {
194         fail = GNUNET_YES;
195       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
196                   _("Missing function `%s' in transport plugin for `%s'\n"),
197                   "check_address",
198                   plug->lib_name);
199     }
200     if (NULL == plug->api->get_session)
201     {
202         fail = GNUNET_YES;
203       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
204                   _("Missing function `%s' in transport plugin for `%s'\n"),
205                   "get_session",
206                   plug->lib_name);
207     }
208     if (NULL == plug->api->get_network)
209     {
210         fail = GNUNET_YES;
211       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
212                   _("Missing function `%s' in transport plugin for `%s'\n"),
213                   "get_network",
214                   plug->lib_name);
215     }
216     if (NULL == plug->api->send)
217     {
218         fail = GNUNET_YES;
219       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
220                   _("Missing function `%s' in transport plugin for `%s'\n"),
221                   "send",
222                   plug->lib_name);
223     }
224     if (NULL == plug->api->disconnect_peer)
225     {
226         fail = GNUNET_YES;
227       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
228                   _("Missing function `%s' in transport plugin for `%s'\n"),
229                   "disconnect_peer",
230                   plug->lib_name);
231     }
232     if (NULL == plug->api->disconnect_session)
233     {
234         fail = GNUNET_YES;
235       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
236                   _("Missing function `%s' in transport plugin for `%s'\n"),
237                   "disconnect_session",
238                   plug->lib_name);
239     }
240     if (NULL == plug->api->query_keepalive_factor)
241     {
242       fail = GNUNET_YES;
243       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
244                   _("Missing function `%s' in transport plugin for `%s'\n"),
245                   "query_keepalive_factor",
246                   plug->lib_name);
247     }
248     if (NULL == plug->api->update_session_timeout)
249     {
250         fail = GNUNET_YES;
251       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
252                   _("Missing function `%s' in transport plugin for `%s'\n"),
253                   "update_session_timeout",
254                   plug->lib_name);
255     }
256     if (GNUNET_YES == fail)
257     {
258       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
259                   _("Did not load plugin `%s' due to missing functions\n"),
260                   plug->lib_name);
261       GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
262       GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
263       GNUNET_free (plug->short_name);
264       GNUNET_free (plug->lib_name);
265       GNUNET_free (plug);
266     }
267   }
268 }
269
270
271 /**
272  * Unload all plugins
273  */
274 void
275 GST_plugins_unload ()
276 {
277   struct TransportPlugin *plug;
278
279   while (NULL != (plug = plugins_head))
280   {
281     GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
282     GNUNET_free (plug->lib_name);
283     GNUNET_free (plug->short_name);
284     GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
285     GNUNET_free (plug);
286   }
287 }
288
289
290 /**
291  * Obtain the plugin API based on a plugin name.
292  *
293  * @param name name of the plugin
294  * @return the plugin's API, NULL if the plugin is not loaded
295  */
296 struct GNUNET_TRANSPORT_PluginFunctions *
297 GST_plugins_find (const char *name)
298 {
299   struct TransportPlugin *head = plugins_head;
300
301   while ((head != NULL) && (0 != strcmp (name, head->short_name)))
302     head = head->next;
303   if (NULL == head)
304     return NULL;
305   return head->api;
306 }
307
308
309 /**
310  * Obtain the plugin API based on a the stripped plugin name after the underscore.
311  *
312  * Example: GST_plugins_printer_find (http_client) will return all plugins
313  * starting with the prefix "http":
314  * http_client or server if loaded
315  *
316  * @param name name of the plugin
317  * @return the plugin's API, NULL if the plugin is not loaded
318  */
319 struct GNUNET_TRANSPORT_PluginFunctions *
320 GST_plugins_printer_find (const char *name)
321 {
322   struct TransportPlugin *head = plugins_head;
323
324   char *stripped = GNUNET_strdup (name);
325   char *sep = strchr (stripped, '_');
326   if (NULL != sep)
327     sep[0] = '\0';
328
329   while (head != NULL)
330   {
331     if (head->short_name == strstr (head->short_name, stripped))
332         break;
333     head = head->next;
334   }
335   GNUNET_free (stripped);
336   if (NULL == head)
337     return NULL;
338   return head->api;
339 }
340
341
342 /**
343  * Convert a given address to a human-readable format.  Note that the
344  * return value will be overwritten on the next call to this function.
345  *
346  * @param address the address to convert
347  * @return statically allocated (!) human-readable address
348  */
349 const char *
350 GST_plugins_a2s (const struct GNUNET_HELLO_Address *address)
351 {
352   struct GNUNET_TRANSPORT_PluginFunctions *api;
353   static char unable_to_show[1024];
354   static const char *s;
355
356   if (address == NULL)
357   {
358         GNUNET_break (0); /* a HELLO address cannot be NULL */
359     return "<invalid>";
360   }
361   if (0 == address->address_length)
362     return TRANSPORT_SESSION_INBOUND_STRING; /* Addresse with length 0 are inbound, address->address itself may be NULL */
363   api = GST_plugins_printer_find (address->transport_name);
364   if (NULL == api)
365     return "<plugin unknown>";
366   if (0 == address->address_length)
367   {
368     GNUNET_snprintf (unable_to_show, sizeof (unable_to_show),
369                      "<unable to stringify %u-byte long address of %s transport>",
370                      (unsigned int) address->address_length,
371                      address->transport_name);
372     return unable_to_show;
373   }
374   return (NULL != (s = api->address_to_string (NULL, address->address,
375                                  address->address_length)) ? s : "<invalid>");
376 }
377
378
379 /* end of file gnunet-service-transport_plugins.c */