peerstore: iterate request timeout
[oweals/gnunet.git] / src / peerstore / gnunet-service-peerstore.c
1 /*
2      This file is part of GNUnet.
3      (C) 
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 peerstore/gnunet-service-peerstore.c
23  * @brief peerstore service implementation
24  * @author Omar Tarabai
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "peerstore.h"
29 #include "gnunet_peerstore_plugin.h"
30 #include "peerstore_common.h"
31
32 //TODO: GNUNET_SERVER_receive_done() ?
33
34 /**
35  * Interval for expired records cleanup (in seconds)
36  */
37 #define CLEANUP_INTERVAL 300 /* 5mins */
38
39 /**
40  * Our configuration.
41  */
42 static const struct GNUNET_CONFIGURATION_Handle *cfg;
43
44 /**
45  * Database plugin library name
46  */
47 char *db_lib_name;
48
49 /**
50  * Database handle
51  */
52 static struct GNUNET_PEERSTORE_PluginFunctions *db;
53
54 /**
55  * Task run during shutdown.
56  *
57  * @param cls unused
58  * @param tc unused
59  */
60 static void
61 shutdown_task (void *cls,
62                const struct GNUNET_SCHEDULER_TaskContext *tc)
63 {
64   if(NULL != db_lib_name)
65   {
66     GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db));
67     GNUNET_free (db_lib_name);
68     db_lib_name = NULL;
69   }
70
71   GNUNET_SCHEDULER_shutdown();
72 }
73
74 /**
75  * Deletes any expired records from storage
76  */
77 static void
78 cleanup_expired_records(void *cls,
79     const struct GNUNET_SCHEDULER_TaskContext *tc)
80 {
81   int deleted;
82
83   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
84     return;
85   GNUNET_assert(NULL != db);
86   deleted = db->expire_records(db->cls, GNUNET_TIME_absolute_get());
87   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "%d records expired.\n", deleted);
88   GNUNET_SCHEDULER_add_delayed(
89       GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, CLEANUP_INTERVAL),
90       &cleanup_expired_records, NULL);
91 }
92
93
94 /**
95  * A client disconnected.  Remove all of its data structure entries.
96  *
97  * @param cls closure, NULL
98  * @param client identification of the client
99  */
100 static void
101 handle_client_disconnect (void *cls,
102                           struct GNUNET_SERVER_Client
103                           * client)
104 {
105 }
106
107 /**
108  * Function called by for each matching record.
109  *
110  * @param cls closure
111  * @param peer peer identity
112  * @param sub_system name of the GNUnet sub system responsible
113  * @param value stored value
114  * @param size size of stored value
115  */
116 int record_iterator(void *cls,
117     struct GNUNET_PEERSTORE_Record *record,
118     char *emsg)
119 {
120   struct GNUNET_SERVER_TransmitContext *tc = cls;
121   struct StoreRecordMessage *srm;
122
123   srm = PEERSTORE_create_record_message(record->sub_system,
124       record->peer,
125       record->key,
126       record->value,
127       record->value_size,
128       record->expiry,
129       GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD);
130   GNUNET_SERVER_transmit_context_append_message(tc, (const struct GNUNET_MessageHeader *)srm);
131   return GNUNET_YES;
132 }
133
134 /**
135  * Handle an iterate request from client
136  *
137  * @param cls unused
138  * @param client identification of the client
139  * @param message the actual message
140  */
141 void handle_iterate (void *cls,
142     struct GNUNET_SERVER_Client *client,
143     const struct GNUNET_MessageHeader *message)
144 {
145   struct GNUNET_PEERSTORE_Record *record;
146   struct GNUNET_SERVER_TransmitContext *tc;
147
148   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Received an iterate request from client.\n");
149   record = PEERSTORE_parse_record_message(message);
150   if(NULL == record)
151   {
152     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Malformed iterate request from client\n");
153     GNUNET_SERVER_receive_done(client, GNUNET_SYSERR);
154     return;
155   }
156   if(NULL == record->sub_system)
157   {
158     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Sub system not supplied in client iterate request\n");
159     GNUNET_SERVER_receive_done(client, GNUNET_SYSERR);
160     return;
161   }
162   tc = GNUNET_SERVER_transmit_context_create (client);
163   if(GNUNET_OK == db->iterate_records(db->cls,
164       record->sub_system,
165       record->peer,
166       record->key,
167       &record_iterator,
168       tc))
169   {
170     GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END);
171     GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
172   }
173   else
174   {
175     GNUNET_free(tc);
176     GNUNET_SERVER_receive_done(client, GNUNET_SYSERR);
177   }
178   GNUNET_free(record);
179 }
180
181 /**
182  * Handle a store request from client
183  *
184  * @param cls unused
185  * @param client identification of the client
186  * @param message the actual message
187  */
188 void handle_store (void *cls,
189     struct GNUNET_SERVER_Client *client,
190     const struct GNUNET_MessageHeader *message)
191 {
192   struct GNUNET_PEERSTORE_Record *record;
193   uint16_t response_type;
194   struct GNUNET_SERVER_TransmitContext *tc;
195
196   record = PEERSTORE_parse_record_message(message);
197   if(NULL == record)
198   {
199     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Malformed store request from client\n");
200     GNUNET_SERVER_receive_done(client, GNUNET_SYSERR);
201     return;
202   }
203   if(NULL == record->sub_system
204       || NULL == record->peer
205       || NULL == record->key)
206   {
207     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Full key not supplied in client store request\n");
208     GNUNET_SERVER_receive_done(client, GNUNET_SYSERR);
209     return;
210   }
211   GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Received a store request (size: %lu) for sub system `%s', peer `%s', key `%s'\n",
212       record->value_size,
213       record->sub_system,
214       GNUNET_i2s (record->peer),
215       record->key);
216   if(GNUNET_OK == db->store_record(db->cls,
217       record->sub_system,
218       record->peer,
219       record->key,
220       record->value,
221       record->value_size,
222       *record->expiry))
223   {
224     response_type = GNUNET_MESSAGE_TYPE_PEERSTORE_STORE_RESULT_OK;
225   }
226   else
227   {
228     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Failed to store requested value, sqlite database error.");
229     response_type = GNUNET_MESSAGE_TYPE_PEERSTORE_STORE_RESULT_FAIL;
230   }
231
232   tc = GNUNET_SERVER_transmit_context_create (client);
233   GNUNET_SERVER_transmit_context_append_data(tc, NULL, 0, response_type);
234   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
235
236 }
237
238 /**
239  * Peerstore service runner.
240  *
241  * @param cls closure
242  * @param server the initialized server
243  * @param c configuration to use
244  */
245 static void
246 run (void *cls,
247      struct GNUNET_SERVER_Handle *server,
248      const struct GNUNET_CONFIGURATION_Handle *c)
249 {
250   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
251       {&handle_store, NULL, GNUNET_MESSAGE_TYPE_PEERSTORE_STORE, 0},
252       {&handle_iterate, NULL, GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE, 0},
253       {NULL, NULL, 0, 0}
254   };
255   char *database;
256
257   cfg = c;
258   if (GNUNET_OK !=
259         GNUNET_CONFIGURATION_get_value_string (cfg, "peerstore", "DATABASE",
260                                                &database))
261     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n");
262
263   else
264   {
265     GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_peerstore_%s", database);
266     db = GNUNET_PLUGIN_load(db_lib_name, (void *) cfg);
267     GNUNET_free(database);
268   }
269   if(NULL == db)
270           GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Could not load database backend `%s'\n", db_lib_name);
271   else
272   {
273     GNUNET_SCHEDULER_add_now(&cleanup_expired_records, NULL);
274     GNUNET_SERVER_add_handlers (server, handlers);
275     GNUNET_SERVER_disconnect_notify (server,
276              &handle_client_disconnect,
277              NULL);
278   }
279   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
280                                 &shutdown_task,
281                                 NULL);
282 }
283
284
285 /**
286  * The main function for the peerstore service.
287  *
288  * @param argc number of arguments from the command line
289  * @param argv command line arguments
290  * @return 0 ok, 1 on error
291  */
292 int
293 main (int argc, char *const *argv)
294 {
295   return (GNUNET_OK ==
296           GNUNET_SERVICE_run (argc,
297                               argv,
298                               "peerstore",
299                               GNUNET_SERVICE_OPTION_NONE,
300                               &run, NULL)) ? 0 : 1;
301 }
302
303 /* end of gnunet-service-peerstore.c */