more FS coding
[oweals/gnunet.git] / src / fs / gnunet-service-fs.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 2, 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 fs/gnunet-service-fs.c
23  * @brief program that provides the file-sharing service
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - INDEX_START handling
28  * - INDEX_LIST handling 
29  * - UNINDEX handling 
30  * - bloomfilter support (GET, CS-request with BF, etc.)
31  * - all P2P messages
32  */
33 #include "platform.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_core_service.h"
36 #include "gnunet_datastore_service.h"
37 #include "gnunet_util_lib.h"
38 #include "fs.h"
39
40 /**
41  * Our connection to the datastore.
42  */
43 static struct GNUNET_DATASTORE_Handle *dsh;
44
45 /**
46  * Our scheduler.
47  */
48 static struct GNUNET_SCHEDULER_Handle *sched;
49
50 /**
51  * Our configuration.
52  */
53 const struct GNUNET_CONFIGURATION_Handle *cfg;
54
55 /**
56  * Handle INDEX_START-message.
57  *
58  * @param cls closure
59  * @param client identification of the client
60  * @param message the actual message
61  */
62 static void
63 handle_index_start (void *cls,
64                     struct GNUNET_SERVER_Client *client,
65                     const struct GNUNET_MessageHeader *message)
66 {
67   const struct IndexStartMessage *ism;
68   const char *fn;
69   uint16_t msize;
70
71   msize = ntohs(message->size);
72   if ( (msize <= sizeof (struct IndexStartMessage)) ||
73        ( ((const char *)message)[msize-1] != '\0') )
74     {
75       GNUNET_break (0);
76       GNUNET_SERVER_receive_done (client,
77                                   GNUNET_SYSERR);
78       return;
79     }
80   ism = (const struct IndexStartMessage*) message;
81   fn = (const char*) &ism[1];
82   // FIXME: store fn, hash, check, respond to client, etc.
83 }
84
85
86 /**
87  * Handle INDEX_LIST_GET-message.
88  *
89  * @param cls closure
90  * @param client identification of the client
91  * @param message the actual message
92  */
93 static void
94 handle_index_list_get (void *cls,
95                        struct GNUNET_SERVER_Client *client,
96                        const struct GNUNET_MessageHeader *message)
97 {
98   struct GNUNET_SERVER_TransmitContext *tc;
99   struct IndexInfoMessage *iim;
100   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
101   size_t slen;
102   char *fn;
103   struct GNUNET_MessageHeader *msg;
104
105   tc = GNUNET_SERVER_transmit_context_create (client);
106   iim = (struct IndexInfoMessage*) buf;
107   msg = &iim->header;
108   while (0)
109     {
110       iim->reserved = 0;
111       // FIXME: read actual list of indexed files...
112       // iim->file_id = id;
113       fn = "FIXME";
114       slen = strlen (fn) + 1;
115       if (slen + sizeof (struct IndexInfoMessage) > 
116           GNUNET_SERVER_MAX_MESSAGE_SIZE)
117         {
118           GNUNET_break (0);
119           break;
120         }
121       memcpy (&iim[1], fn, slen);
122       GNUNET_SERVER_transmit_context_append
123         (tc,
124          &msg[1],
125          sizeof (struct IndexInfoMessage) 
126          - sizeof (struct GNUNET_MessageHeader) + slen,
127          GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY);
128     }
129   GNUNET_SERVER_transmit_context_append (tc,
130                                          NULL, 0,
131                                          GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END);
132   GNUNET_SERVER_transmit_context_run (tc,
133                                       GNUNET_TIME_UNIT_MINUTES);
134 }
135
136
137 /**
138  * Handle UNINDEX-message.
139  *
140  * @param cls closure
141  * @param client identification of the client
142  * @param message the actual message
143  */
144 static void
145 handle_unindex (void *cls,
146                 struct GNUNET_SERVER_Client *client,
147                 const struct GNUNET_MessageHeader *message)
148 {
149   const struct UnindexMessage *um;
150   struct GNUNET_SERVER_TransmitContext *tc;
151   
152   um = (const struct UnindexMessage*) message;
153   // fixme: process!
154   tc = GNUNET_SERVER_transmit_context_create (client);
155   GNUNET_SERVER_transmit_context_append (tc,
156                                          NULL, 0,
157                                          GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK);
158   GNUNET_SERVER_transmit_context_run (tc,
159                                       GNUNET_TIME_UNIT_MINUTES);
160 }
161
162
163 /**
164  * Signature of a function that is called whenever a datastore
165  * request can be processed (or an entry put on the queue times out).
166  *
167  * @param cls closure
168  * @param ok GNUNET_OK if DS is ready, GNUNET_SYSERR on timeout
169  */
170 typedef void (*RequestFunction)(void *cls,
171                                 int ok);
172
173
174 /**
175  * Doubly-linked list of our requests for the datastore.
176  */
177 struct DatastoreRequestQueue
178 {
179
180   /**
181    * This is a doubly-linked list.
182    */
183   struct DatastoreRequestQueue *next;
184
185   /**
186    * This is a doubly-linked list.
187    */
188   struct DatastoreRequestQueue *prev;
189
190   /**
191    * Function to call (will issue the request).
192    */
193   RequestFunction req;
194
195   /**
196    * Closure for req.
197    */
198   void *req_cls;
199
200   /**
201    * When should this request time-out because we don't care anymore?
202    */
203   struct GNUNET_TIME_Absolute timeout;
204     
205   /**
206    * ID of task used for signaling timeout.
207    */
208   GNUNET_SCHEDULER_TaskIdentifier task;
209
210 };
211
212
213 /**
214  * Head of request queue for the datastore, sorted by timeout.
215  */
216 static struct DatastoreRequestQueue *drq_head;
217
218 /**
219  * Tail of request queue for the datastore.
220  */
221 static struct DatastoreRequestQueue *drq_tail;
222
223
224 /**
225  * Run the next DS request in our
226  * queue, we're done with the current one.
227  */
228 static void
229 next_ds_request ()
230 {
231   struct DatastoreRequestQueue *e;
232   
233   while (NULL != (e = drq_head))
234     {
235       if (0 != GNUNET_TIME_absolute_get_remaining (e->timeout).value)
236         break;
237       if (e->task != GNUNET_SCHEDULER_NO_TASK)
238         GNUNET_SCHEDULER_cancel (sched, e->task);
239       GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
240       e->req (e->req_cls, GNUNET_NO);
241       GNUNET_free (e);  
242     }
243   if (e == NULL)
244     return;
245   if (e->task != GNUNET_SCHEDULER_NO_TASK)
246     GNUNET_SCHEDULER_cancel (sched, e->task);
247   e->task = GNUNET_SCHEDULER_NO_TASK;
248   e->req (e->req_cls, GNUNET_YES);
249   GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
250   GNUNET_free (e);  
251 }
252
253
254 /**
255  * A datastore request had to be timed out. 
256  *
257  * @param cls closure (of type "struct DatastoreRequestQueue*")
258  * @param tc task context, unused
259  */
260 static void
261 timeout_ds_request (void *cls,
262                     const struct GNUNET_SCHEDULER_TaskContext *tc)
263 {
264   struct DatastoreRequestQueue *e = cls;
265
266   e->task = GNUNET_SCHEDULER_NO_TASK;
267   GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
268   e->req (e->req_cls, GNUNET_NO);
269   GNUNET_free (e);  
270 }
271
272
273 /**
274  * Queue a request for the datastore.
275  *
276  * @param deadline by when the request should run
277  * @param fun function to call once the request can be run
278  * @param fun_cls closure for fun
279  */
280 static struct DatastoreRequestQueue *
281 queue_ds_request (struct GNUNET_TIME_Relative deadline,
282                   RequestFunction fun,
283                   void *fun_cls)
284 {
285   struct DatastoreRequestQueue *e;
286   struct DatastoreRequestQueue *bef;
287
288   if (drq_head == NULL)
289     {
290       /* no other requests pending, run immediately */
291       fun (fun_cls, GNUNET_OK);
292       return NULL;
293     }
294   e = GNUNET_malloc (sizeof (struct DatastoreRequestQueue));
295   e->timeout = GNUNET_TIME_relative_to_absolute (deadline);
296   e->req = fun;
297   e->req_cls = fun_cls;
298   if (deadline.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
299     {
300       /* local request, highest prio, put at head of queue
301          regardless of deadline */
302       bef = NULL;
303     }
304   else
305     {
306       bef = drq_tail;
307       while ( (NULL != bef) &&
308               (e->timeout.value < bef->timeout.value) )
309         bef = bef->prev;
310     }
311   GNUNET_CONTAINER_DLL_insert_after (drq_head, drq_tail, bef, e);
312   if (deadline.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
313     return e;
314   e->task = GNUNET_SCHEDULER_add_delayed (sched,
315                                           GNUNET_NO,
316                                           GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
317                                           GNUNET_SCHEDULER_NO_TASK,
318                                           deadline,
319                                           &timeout_ds_request,
320                                           e);
321   return e;                                    
322 }
323
324
325 /**
326  * Closure for processing START_SEARCH messages from a client.
327  */
328 struct LocalGetContext
329 {
330
331   /**
332    * This is a doubly-linked list.
333    */
334   struct LocalGetContext *next;
335
336   /**
337    * This is a doubly-linked list.
338    */
339   struct LocalGetContext *prev;
340
341   /**
342    * Client that initiated the search.
343    */
344   struct GNUNET_SERVER_Client *client;
345
346   /**
347    * Array of results that we've already received 
348    * (can be NULL).
349    */
350   GNUNET_HashCode *results; 
351
352   /**
353    * Bloomfilter over all results (for fast query construction);
354    * NULL if we don't have any results.
355    */
356   struct GNUNET_CONTAINER_BloomFilter *results_bf; 
357
358   /**
359    * DS request associated with this operation.
360    */
361   struct DatastoreRequestQueue *req;
362
363   /**
364    * Current result message to transmit to client (or NULL).
365    */
366   struct ContentMessage *result;
367   
368   /**
369    * Type of the content that we're looking for.
370    * 0 for any.
371    */
372   uint32_t type;
373
374   /**
375    * Desired anonymity level.
376    */
377   uint32_t anonymity_level;
378
379   /**
380    * Number of results actually stored in the results array.
381    */
382   unsigned int results_used;
383   
384   /**
385    * Size of the results array in memory.
386    */
387   unsigned int results_size;
388
389   /**
390    * If the request is for a DBLOCK or IBLOCK, this is the identity of
391    * the peer that is known to have a response.  Set to all-zeros if
392    * such a target is not known (note that even if OUR anonymity
393    * level is >0 we may happen to know the responder's identity;
394    * nevertheless, we should probably not use it for a DHT-lookup
395    * or similar blunt actions in order to avoid exposing ourselves).
396    * <p>
397    * If the request is for an SBLOCK, this is the identity of the
398    * pseudonym to which the SBLOCK belongs. 
399    * <p>
400    * If the request is for a KBLOCK, "target" must be all zeros.
401    */
402   GNUNET_HashCode target;
403
404   /**
405    * Hash of the keyword (aka query) for KBLOCKs; Hash of
406    * the CHK-encoded block for DBLOCKS and IBLOCKS (aka query)
407    * and hash of the identifier XORed with the target for
408    * SBLOCKS (aka query).
409    */
410   GNUNET_HashCode query;
411
412 };
413
414
415 /**
416  * Head of doubly-linked LGC list.
417  */
418 static struct LocalGetContext *lgc_head;
419
420 /**
421  * Tail of doubly-linked LGC list.
422  */
423 static struct LocalGetContext *lgc_tail;
424
425
426 /**
427  * Free the state associated with a local get context.
428  *
429  * @param lgc the lgc to free
430  */
431 static void
432 local_get_context_free (struct LocalGetContext *lgc) 
433 {
434   GNUNET_CONTAINER_DLL_remove (lgc_head, lgc_tail, lgc);
435   GNUNET_SERVER_client_drop (lgc->client); 
436   GNUNET_free_non_null (lgc->results);
437   if (lgc->results_bf != NULL)
438     GNUNET_CONTAINER_bloomfilter_free (lgc->results_bf);
439   if (lgc->req != NULL)
440     {
441       if (lgc->req->task != GNUNET_SCHEDULER_NO_TASK)
442         GNUNET_SCHEDULER_cancel (sched, lgc->req->task);
443       GNUNET_CONTAINER_DLL_remove (lgc_head, lgc_tail, lgc);
444       GNUNET_free (lgc->req);
445     }
446   GNUNET_free (lgc);
447 }
448
449
450 /**
451  * We're able to transmit the next (local) result to the client.
452  * Do it and ask the datastore for more.  Or, on error, tell
453  * the datastore to stop giving us more.
454  *
455  * @param cls our closure (struct LocalGetContext)
456  * @param max maximum number of bytes we can transmit
457  * @param buf where to copy our message
458  * @return number of bytes copied to buf
459  */
460 static size_t
461 transmit_local_result (void *cls,
462                        size_t max,
463                        void *buf)
464 {
465   struct LocalGetContext *lgc = cls;  
466   uint16_t msize;
467
468   if (NULL == buf)
469     {
470       /* error, abort! */
471       GNUNET_free (lgc->result);
472       lgc->result = NULL;
473       GNUNET_DATASTORE_get_next (dsh, GNUNET_NO);
474       return 0;
475     }
476   msize = ntohs (lgc->result->header.size);
477   GNUNET_assert (max >= msize);
478   memcpy (buf, lgc->result, msize);
479   GNUNET_free (lgc->result);
480   lgc->result = NULL;
481   GNUNET_DATASTORE_get_next (dsh, GNUNET_YES);
482   return msize;
483 }
484
485
486 /**
487  * We're processing (local) results for a search request
488  * from a (local) client.  Pass applicable results to the
489  * client and if we are done either clean up (operation
490  * complete) or switch to P2P search (more results possible).
491  *
492  * @param cls our closure (struct LocalGetContext)
493  * @param key key for the content
494  * @param size number of bytes in data
495  * @param data content stored
496  * @param type type of the content
497  * @param priority priority of the content
498  * @param anonymity anonymity-level for the content
499  * @param expiration expiration time for the content
500  * @param uid unique identifier for the datum;
501  *        maybe 0 if no unique identifier is available
502  */
503 static void
504 process_local_get_result (void *cls,
505                           const GNUNET_HashCode * key,
506                           uint32_t size,
507                           const void *data,
508                           uint32_t type,
509                           uint32_t priority,
510                           uint32_t anonymity,
511                           struct GNUNET_TIME_Absolute
512                           expiration, 
513                           uint64_t uid)
514 {
515   struct LocalGetContext *lgc = cls;
516   size_t msize;
517   
518   if (key == NULL)
519     {
520       /* no further results from datastore; continue
521          processing further requests from the client and
522          allow the next task to use the datastore; also,
523          switch to P2P requests or clean up our state. */
524       next_ds_request ();
525       GNUNET_SERVER_receive_done (lgc->client,
526                                   GNUNET_OK);
527       if ( (lgc->results_used == 0) ||
528            (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_KBLOCK) ||
529            (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_SBLOCK) ||
530            (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_SKBLOCK) )
531         {
532           // FIXME: initiate P2P search
533           return;
534         }
535       /* got all possible results, clean up! */
536       local_get_context_free (lgc);
537       return;
538     }
539   if (lgc->results_used == lgc->results_size)
540     {
541       GNUNET_array_grow (lgc->results,
542                          lgc->results_size,
543                          lgc->results_size * 2 + 2);
544       if ( (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) ||
545            (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_IBLOCK) )
546         {
547           // FIXME: possibly grow/create BF!
548         }
549     }
550   GNUNET_CRYPTO_hash (data, 
551                       size, 
552                       &lgc->results[lgc->results_used++]);    
553   if ( (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) ||
554        (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_IBLOCK) )
555     {
556       // FIXME: add result to BF!
557     }
558   msize = size + sizeof (struct ContentMessage);
559   GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
560   lgc->result = GNUNET_malloc (msize);
561   lgc->result->header.size = htons (msize);
562   lgc->result->header.type = htons (GNUNET_MESSAGE_TYPE_FS_CONTENT);
563   lgc->result->type = htonl (type);
564   lgc->result->expiration = GNUNET_TIME_absolute_hton (expiration);
565   memcpy (&lgc->result[1],
566           data,
567           size);
568   GNUNET_SERVER_notify_transmit_ready (lgc->client,
569                                        msize,
570                                        GNUNET_TIME_UNIT_FOREVER_REL,
571                                        &transmit_local_result,
572                                        lgc);
573 }
574
575
576 /**
577  * We're processing a search request from a local
578  * client.  Now it is our turn to query the datastore.
579  * 
580  * @param cls our closure (struct LocalGetContext)
581  * @param tc unused
582  */
583 static void
584 transmit_local_get (void *cls,
585                     const struct GNUNET_SCHEDULER_TaskContext *tc)
586 {
587   struct LocalGetContext *lgc = cls;
588
589   GNUNET_DATASTORE_get (dsh,
590                         &lgc->query,
591                         lgc->type,
592                         &process_local_get_result,
593                         lgc,
594                         GNUNET_TIME_UNIT_FOREVER_REL);
595 }
596
597
598 /**
599  * We're processing a search request from a local
600  * client.  Now it is our turn to query the datastore.
601  * 
602  * @param cls our closure (struct LocalGetContext)
603  * @param ok did we succeed to queue for datastore access, should always be GNUNET_OK
604  */
605 static void 
606 transmit_local_get_ready (void *cls,
607                           int ok)
608 {
609   struct LocalGetContext *lgc = cls;
610
611   GNUNET_assert (GNUNET_OK == ok);
612   GNUNET_SCHEDULER_add_continuation (sched,
613                                      GNUNET_NO,
614                                      &transmit_local_get,
615                                      lgc,
616                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
617 }
618
619
620 /**
621  * Handle START_SEARCH-message (search request from client).
622  *
623  * @param cls closure
624  * @param client identification of the client
625  * @param message the actual message
626  */
627 static void
628 handle_start_search (void *cls,
629                      struct GNUNET_SERVER_Client *client,
630                      const struct GNUNET_MessageHeader *message)
631 {
632   const struct SearchMessage *sm;
633   struct LocalGetContext *lgc;
634
635   sm = (const struct SearchMessage*) message;
636   GNUNET_SERVER_client_keep (client);
637   lgc = GNUNET_malloc (sizeof (struct LocalGetContext));
638   lgc->client = client;
639   lgc->type = ntohl (sm->type);
640   lgc->anonymity_level = ntohl (sm->anonymity_level);
641   lgc->target = sm->target;
642   lgc->query = sm->query;
643   GNUNET_CONTAINER_DLL_insert (lgc_head, lgc_tail, lgc);
644   lgc->req = queue_ds_request (GNUNET_TIME_UNIT_FOREVER_REL,
645                                &transmit_local_get_ready,
646                                lgc);
647 }
648
649
650 /**
651  * List of handlers for the messages understood by this
652  * service.
653  */
654 static struct GNUNET_SERVER_MessageHandler handlers[] = {
655   {&handle_index_start, NULL, 
656    GNUNET_MESSAGE_TYPE_FS_INDEX_START, 0},
657   {&handle_index_list_get, NULL, 
658    GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET, sizeof(struct GNUNET_MessageHeader) },
659   {&handle_unindex, NULL, GNUNET_MESSAGE_TYPE_FS_UNINDEX, 
660    sizeof (struct UnindexMessage) },
661   {&handle_start_search, NULL, GNUNET_MESSAGE_TYPE_FS_START_SEARCH, 
662    sizeof (struct SearchMessage) },
663   {NULL, NULL, 0, 0}
664 };
665
666
667 /**
668  * A client disconnected.  Remove all of its pending queries.
669  *
670  * @param cls closure, NULL
671  * @param client identification of the client
672  */
673 static void
674 handle_client_disconnect (void *cls,
675                           struct GNUNET_SERVER_Client
676                           * client)
677 {
678   struct LocalGetContext *lgc;
679
680   lgc = lgc_head;
681   while ( (NULL != lgc) &&
682           (lgc->client != client) )
683     lgc = lgc->next;
684   if (lgc == NULL)
685     return; /* not one of our clients */
686   local_get_context_free (lgc);
687 }
688
689
690 /**
691  * Task run during shutdown.
692  *
693  * @param cls unused
694  * @param tc unused
695  */
696 static void
697 shutdown_task (void *cls,
698                const struct GNUNET_SCHEDULER_TaskContext *tc)
699 {
700   GNUNET_DATASTORE_disconnect (dsh,
701                                GNUNET_NO);
702   dsh = NULL;
703 }
704
705
706 /**
707  * Process fs requests.
708  *
709  * @param cls closure
710  * @param sched scheduler to use
711  * @param server the initialized server
712  * @param cfg configuration to use
713  */
714 static void
715 run (void *cls,
716      struct GNUNET_SCHEDULER_Handle *s,
717      struct GNUNET_SERVER_Handle *server,
718      const struct GNUNET_CONFIGURATION_Handle *c)
719 {
720   sched = s;
721   cfg = c;
722   dsh = GNUNET_DATASTORE_connect (cfg,
723                                   sched);
724   if (NULL == dsh)
725     {
726       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
727                   _("Failed to connect to datastore service.\n"));
728       return;
729     }
730   GNUNET_SERVER_disconnect_notify (server, 
731                                    &handle_client_disconnect,
732                                    NULL);
733   GNUNET_SERVER_add_handlers (server, handlers);
734   // FIXME: also register with core to handle P2P messages!
735   GNUNET_SCHEDULER_add_delayed (sched,
736                                 GNUNET_YES,
737                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
738                                 GNUNET_SCHEDULER_NO_TASK,
739                                 GNUNET_TIME_UNIT_FOREVER_REL,
740                                 &shutdown_task,
741                                 NULL);
742 }
743
744
745 /**
746  * The main function for the fs service.
747  *
748  * @param argc number of arguments from the command line
749  * @param argv command line arguments
750  * @return 0 ok, 1 on error
751  */
752 int
753 main (int argc, char *const *argv)
754 {
755   return (GNUNET_OK ==
756           GNUNET_SERVICE_run (argc,
757                               argv,
758                               "fs", &run, NULL, NULL, NULL)) ? 0 : 1;
759 }
760
761 /* end of gnunet-service-fs.c */