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