psyc, psycstore: retrieve state and history
[oweals/gnunet.git] / src / psycstore / gnunet-service-psycstore.c
1 /*
2  * This file is part of GNUnet
3  * (C) 2013 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 psycstore/gnunet-service-psycstore.c
23  * @brief PSYCstore service
24  * @author Gabor X Toth
25  * @author Christian Grothoff
26  */
27
28 #include <inttypes.h>
29
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32 #include "gnunet_constants.h"
33 #include "gnunet_protocols.h"
34 #include "gnunet_statistics_service.h"
35 #include "gnunet_psycstore_service.h"
36 #include "gnunet_psycstore_plugin.h"
37 #include "psycstore.h"
38
39
40 /**
41  * Handle to our current configuration.
42  */
43 static const struct GNUNET_CONFIGURATION_Handle *cfg;
44
45 /**
46  * Handle to the statistics service.
47  */
48 static struct GNUNET_STATISTICS_Handle *stats;
49
50 /**
51  * Notification context, simplifies client broadcasts.
52  */
53 static struct GNUNET_SERVER_NotificationContext *nc;
54
55 /**
56  * Database handle
57  */
58 static struct GNUNET_PSYCSTORE_PluginFunctions *db;
59
60 /**
61  * Name of the database plugin
62  */
63 static char *db_lib_name;
64
65
66 /**
67  * Task run during shutdown.
68  *
69  * @param cls unused
70  * @param tc unused
71  */
72 static void
73 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
74 {
75   if (NULL != nc)
76   {
77     GNUNET_SERVER_notification_context_destroy (nc);
78     nc = NULL;
79   }
80   if (NULL != stats)
81   {
82     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
83     stats = NULL;
84   }
85   GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db));
86   GNUNET_free (db_lib_name);
87   db_lib_name = NULL;
88 }
89
90
91 /**
92  * Send a result code back to the client.
93  *
94  * @param client
95  *        Client that should receive the result code.
96  * @param result_code
97  *        Code to transmit.
98  * @param op_id
99  *        Operation ID in network byte order.
100  * @param err_msg
101  *        Error message to include (or NULL for none).
102  */
103 static void
104 send_result_code (struct GNUNET_SERVER_Client *client, uint64_t op_id,
105                   int64_t result_code, const char *err_msg)
106 {
107   struct OperationResult *res;
108   size_t err_len = 0; // FIXME: maximum length
109
110   if (NULL != err_msg)
111     err_len = strlen (err_msg) + 1;
112   res = GNUNET_malloc (sizeof (struct OperationResult) + err_len);
113   res->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE);
114   res->header.size = htons (sizeof (struct OperationResult) + err_len);
115   res->result_code = GNUNET_htonll (result_code - INT64_MIN);
116   res->op_id = op_id;
117   if (0 < err_len)
118     memcpy (&res[1], err_msg, err_len);
119   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
120               "Sending result to client: %" PRId64 " (%s)\n",
121               result_code, err_msg);
122   GNUNET_SERVER_notification_context_add (nc, client);
123   GNUNET_SERVER_notification_context_unicast (nc, client, &res->header,
124                                               GNUNET_NO);
125   GNUNET_free (res);
126 }
127
128
129 enum
130 {
131   MEMBERSHIP_TEST_NOT_NEEDED = 0,
132   MEMBERSHIP_TEST_NEEDED = 1,
133   MEMBERSHIP_TEST_DONE = 2,
134 } MessageMembershipTest;
135
136
137 struct SendClosure
138 {
139   struct GNUNET_SERVER_Client *client;
140
141   /**
142    * Channel's public key.
143    */
144   struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
145
146   /**
147    * Slave's public key.
148    */
149   struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
150
151   /**
152    * Operation ID.
153    */
154   uint64_t op_id;
155
156   /**
157    * Membership test result.
158    */
159   int membership_test_result;
160
161   /**
162    * Do membership test with @a slave_key before returning fragment?
163    * @see enum MessageMembershipTest
164    */
165   uint8_t membership_test;
166 };
167
168
169 static int
170 send_fragment (void *cls, struct GNUNET_MULTICAST_MessageHeader *msg,
171                enum GNUNET_PSYCSTORE_MessageFlags flags)
172 {
173   struct SendClosure *sc = cls;
174   struct FragmentResult *res;
175
176   if (MEMBERSHIP_TEST_NEEDED == sc->membership_test)
177   {
178     sc->membership_test = MEMBERSHIP_TEST_DONE;
179     sc->membership_test_result
180       = db->membership_test (db->cls, &sc->channel_key, &sc->slave_key,
181                              GNUNET_ntohll (msg->message_id));
182     switch (sc->membership_test_result)
183     {
184     case GNUNET_YES:
185       break;
186
187     case GNUNET_NO:
188     case GNUNET_SYSERR:
189       return GNUNET_NO;
190     }
191   }
192
193   size_t msg_size = ntohs (msg->header.size);
194
195   res = GNUNET_malloc (sizeof (struct FragmentResult) + msg_size);
196   res->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_FRAGMENT);
197   res->header.size = htons (sizeof (struct FragmentResult) + msg_size);
198   res->op_id = sc->op_id;
199   res->psycstore_flags = htonl (flags);
200   memcpy (&res[1], msg, msg_size);
201   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
202               "Sending fragment %ld to client\n",
203               GNUNET_ntohll (msg->fragment_id));
204   GNUNET_free (msg);
205   GNUNET_SERVER_notification_context_add (nc, sc->client);
206   GNUNET_SERVER_notification_context_unicast (nc, sc->client, &res->header,
207                                               GNUNET_NO);
208   GNUNET_free (res);
209   return GNUNET_YES;
210 }
211
212
213 static int
214 send_state_var (void *cls, const char *name,
215                 const void *value, size_t value_size)
216 {
217   struct SendClosure *sc = cls;
218   struct StateResult *res;
219   size_t name_size = strlen (name) + 1;
220
221   /* FIXME: split up value into 64k chunks */
222
223   res = GNUNET_malloc (sizeof (struct StateResult) + name_size + value_size);
224   res->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_STATE);
225   res->header.size = htons (sizeof (struct StateResult) + name_size + value_size);
226   res->op_id = sc->op_id;
227   res->name_size = htons (name_size);
228   memcpy (&res[1], name, name_size);
229   memcpy ((char *) &res[1] + name_size, value, value_size);
230   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
231               "Sending state variable %s to client\n", name);
232   GNUNET_SERVER_notification_context_add (nc, sc->client);
233   GNUNET_SERVER_notification_context_unicast (nc, sc->client, &res->header,
234                                               GNUNET_NO);
235   GNUNET_free (res);
236   return GNUNET_OK;
237 }
238
239
240 static void
241 handle_membership_store (void *cls,
242                          struct GNUNET_SERVER_Client *client,
243                          const struct GNUNET_MessageHeader *msg)
244 {
245   const struct MembershipStoreRequest *req =
246     (const struct MembershipStoreRequest *) msg;
247
248   int ret = db->membership_store (db->cls, &req->channel_key, &req->slave_key,
249                                   req->did_join,
250                                   GNUNET_ntohll (req->announced_at),
251                                   GNUNET_ntohll (req->effective_since),
252                                   GNUNET_ntohll (req->group_generation));
253
254   if (ret != GNUNET_OK)
255     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
256                 _("Failed to store membership information!\n"));
257
258   send_result_code (client, req->op_id, ret, NULL);
259   GNUNET_SERVER_receive_done (client, GNUNET_OK);
260 }
261
262
263 static void
264 handle_membership_test (void *cls,
265                         struct GNUNET_SERVER_Client *client,
266                         const struct GNUNET_MessageHeader *msg)
267 {
268   const struct MembershipTestRequest *req =
269     (const struct MembershipTestRequest *) msg;
270
271   int ret = db->membership_test (db->cls, &req->channel_key, &req->slave_key,
272                                  GNUNET_ntohll (req->message_id));
273   switch (ret)
274   {
275   case GNUNET_YES:
276   case GNUNET_NO:
277     break;
278   default:
279     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
280                 _("Failed to test membership!\n"));
281   }
282
283   send_result_code (client, req->op_id, ret, NULL);
284   GNUNET_SERVER_receive_done (client, GNUNET_OK);
285 }
286
287
288 static void
289 handle_fragment_store (void *cls,
290                        struct GNUNET_SERVER_Client *client,
291                        const struct GNUNET_MessageHeader *msg)
292 {
293   const struct FragmentStoreRequest *req =
294     (const struct FragmentStoreRequest *) msg;
295
296   int ret = db->fragment_store (db->cls, &req->channel_key,
297                                 (const struct GNUNET_MULTICAST_MessageHeader *)
298                                 &req[1], ntohl (req->psycstore_flags));
299
300   if (ret != GNUNET_OK)
301     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
302                 _("Failed to store fragment!\n"));
303
304   send_result_code (client, req->op_id, ret, NULL);
305   GNUNET_SERVER_receive_done (client, GNUNET_OK);
306 }
307
308
309 static void
310 handle_fragment_get (void *cls,
311                      struct GNUNET_SERVER_Client *client,
312                      const struct GNUNET_MessageHeader *msg)
313 {
314   const struct FragmentGetRequest *
315     req = (const struct FragmentGetRequest *) msg;
316   struct SendClosure
317     sc = { .op_id = req->op_id, .client = client,
318            .channel_key = req->channel_key, .slave_key = req->slave_key,
319            .membership_test = req->do_membership_test };
320
321   int64_t ret;
322   uint64_t ret_frags = 0;
323   uint64_t first_fragment_id = GNUNET_ntohll (req->first_fragment_id);
324   uint64_t last_fragment_id = GNUNET_ntohll (req->last_fragment_id);
325   uint64_t limit = GNUNET_ntohll (req->fragment_limit);
326
327   if (0 == limit)
328     ret = db->fragment_get (db->cls, &req->channel_key,
329                             first_fragment_id, last_fragment_id,
330                             &ret_frags, &send_fragment, &sc);
331   else
332     ret = db->fragment_get_latest (db->cls, &req->channel_key, limit, 
333                                    &ret_frags, &send_fragment, &sc);
334
335   switch (ret)
336   {
337   case GNUNET_YES:
338   case GNUNET_NO:
339     if (MEMBERSHIP_TEST_DONE == sc.membership_test)
340     {
341       switch (sc.membership_test_result)
342       {
343       case GNUNET_YES:
344         break;
345
346       case GNUNET_NO:
347         ret = GNUNET_PSYCSTORE_MEMBERSHIP_TEST_FAILED;
348         break;
349
350       case GNUNET_SYSERR:
351         ret = GNUNET_SYSERR;
352         break;
353       }
354     }
355     break;
356   default:
357     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
358                 _("Failed to get fragment!\n"));
359   }
360   send_result_code (client, req->op_id, (ret < 0) ? ret : ret_frags, NULL);
361   GNUNET_SERVER_receive_done (client, GNUNET_OK);
362 }
363
364
365 static void
366 handle_message_get (void *cls,
367                     struct GNUNET_SERVER_Client *client,
368                     const struct GNUNET_MessageHeader *msg)
369 {
370   const struct MessageGetRequest *
371     req = (const struct MessageGetRequest *) msg;
372   struct SendClosure
373     sc = { .op_id = req->op_id, .client = client,
374            .channel_key = req->channel_key, .slave_key = req->slave_key,
375            .membership_test = req->do_membership_test };
376
377   int64_t ret;
378   uint64_t ret_frags = 0;
379   uint64_t first_message_id = GNUNET_ntohll (req->first_message_id);
380   uint64_t last_message_id = GNUNET_ntohll (req->last_message_id);
381   uint64_t limit = GNUNET_ntohll (req->message_limit);
382
383   if (0 == limit)
384     ret = db->message_get (db->cls, &req->channel_key,
385                            first_message_id, last_message_id,
386                            &ret_frags, &send_fragment, &sc);
387   else
388     ret = db->message_get_latest (db->cls, &req->channel_key, limit,
389                                   &ret_frags, &send_fragment, &sc);
390
391   switch (ret)
392   {
393   case GNUNET_YES:
394   case GNUNET_NO:
395     break;
396   default:
397     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
398                 _("Failed to get message!\n"));
399   }
400
401   send_result_code (client, req->op_id, (ret < 0) ? ret : ret_frags, NULL);
402   GNUNET_SERVER_receive_done (client, GNUNET_OK);
403 }
404
405
406 static void
407 handle_message_get_fragment (void *cls,
408                              struct GNUNET_SERVER_Client *client,
409                              const struct GNUNET_MessageHeader *msg)
410 {
411   const struct MessageGetFragmentRequest *
412     req = (const struct MessageGetFragmentRequest *) msg;
413   struct SendClosure
414     sc = { .op_id = req->op_id, .client = client,
415            .channel_key = req->channel_key, .slave_key = req->slave_key,
416            .membership_test = req->do_membership_test };
417
418   int ret = db->message_get_fragment (db->cls, &req->channel_key,
419                                       GNUNET_ntohll (req->message_id),
420                                       GNUNET_ntohll (req->fragment_offset),
421                                       &send_fragment, &sc);
422   switch (ret)
423   {
424   case GNUNET_YES:
425   case GNUNET_NO:
426     break;
427   default:
428     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429                 _("Failed to get message fragment!\n"));
430   }
431
432   send_result_code (client, req->op_id, ret, NULL);
433   GNUNET_SERVER_receive_done (client, GNUNET_OK);
434 }
435
436
437 static void
438 handle_counters_get (void *cls,
439                      struct GNUNET_SERVER_Client *client,
440                      const struct GNUNET_MessageHeader *msg)
441 {
442   const struct OperationRequest *req = (const struct OperationRequest *) msg;
443   struct CountersResult res = { {0} };
444
445   int ret = db->counters_message_get (db->cls, &req->channel_key,
446                                       &res.max_fragment_id, &res.max_message_id,
447                                       &res.max_group_generation);
448   switch (ret)
449   {
450   case GNUNET_OK:
451     ret = db->counters_state_get (db->cls, &req->channel_key,
452                                   &res.max_state_message_id);
453   case GNUNET_NO:
454     break;
455   default:
456     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
457                 _("Failed to get master counters!\n"));
458   }
459
460   res.header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_COUNTERS);
461   res.header.size = htons (sizeof (res));
462   res.result_code = htonl (ret - INT32_MIN);
463   res.op_id = req->op_id;
464   res.max_fragment_id = GNUNET_htonll (res.max_fragment_id);
465   res.max_message_id = GNUNET_htonll (res.max_message_id);
466   res.max_group_generation = GNUNET_htonll (res.max_group_generation);
467   res.max_state_message_id = GNUNET_htonll (res.max_state_message_id);
468
469   GNUNET_SERVER_notification_context_add (nc, client);
470   GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
471                                               GNUNET_NO);
472
473   GNUNET_SERVER_receive_done (client, GNUNET_OK);
474 }
475
476
477 /* FIXME: stop processing further state modify messages after an error */
478 static void
479 handle_state_modify (void *cls,
480                      struct GNUNET_SERVER_Client *client,
481                      const struct GNUNET_MessageHeader *msg)
482 {
483   const struct StateModifyRequest *req
484     = (const struct StateModifyRequest *) msg;
485
486   int ret = GNUNET_SYSERR;
487   const char *name = (const char *) &req[1];
488   uint16_t name_size = ntohs (req->name_size);
489
490   if (name_size <= 2 || '\0' != name[name_size - 1])
491   {
492     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
493                 _("Tried to set invalid state variable name!\n"));
494     GNUNET_break_op (0);
495   }
496   else
497   {
498     ret = GNUNET_OK;
499
500     if (req->flags & STATE_OP_FIRST)
501     {
502       ret = db->state_modify_begin (db->cls, &req->channel_key,
503                                     GNUNET_ntohll (req->message_id),
504                                     GNUNET_ntohll (req->state_delta));
505     }
506     if (ret != GNUNET_OK)
507     {
508       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
509                   _("Failed to begin modifying state!\n"));
510     }
511     else
512     {
513       switch (req->oper)
514       {
515       case GNUNET_ENV_OP_ASSIGN:
516         ret = db->state_modify_set (db->cls, &req->channel_key,
517                                     (const char *) &req[1],
518                                     name + ntohs (req->name_size),
519                                     ntohs (req->header.size) - sizeof (*req)
520                                     - ntohs (req->name_size));
521         break;
522       default:
523 #if TODO
524         ret = GNUNET_ENV_operation ((const char *) &req[1],
525                                     current_value, current_value_size,
526                                     req->oper, name + ntohs (req->name_size),
527                                     ntohs (req->header.size) - sizeof (*req)
528                                     - ntohs (req->name_size), &value, &value_size);
529 #endif
530         ret = GNUNET_SYSERR;
531         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
532                     _("Unknown operator: %c\n"), req->oper);
533       }
534     }
535
536     if (GNUNET_OK == ret && req->flags & STATE_OP_LAST)
537     {
538       ret = db->state_modify_end (db->cls, &req->channel_key,
539                                   GNUNET_ntohll (req->message_id));
540       if (ret != GNUNET_OK)
541         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
542                     _("Failed to end modifying state!\n"));
543     }
544   }
545   send_result_code (client, req->op_id, ret, NULL);
546   GNUNET_SERVER_receive_done (client, GNUNET_OK);
547 }
548
549
550 /* FIXME: stop processing further state sync messages after an error */
551 static void
552 handle_state_sync (void *cls,
553                    struct GNUNET_SERVER_Client *client,
554                    const struct GNUNET_MessageHeader *msg)
555 {
556   const struct StateSyncRequest *req
557     = (const struct StateSyncRequest *) msg;
558
559   int ret = GNUNET_SYSERR;
560   const char *name = (const char *) &req[1];
561   uint16_t name_size = ntohs (req->name_size);
562
563   if (name_size <= 2 || '\0' != name[name_size - 1])
564   {
565     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
566                 _("Tried to set invalid state variable name!\n"));
567     GNUNET_break_op (0);
568   }
569   else
570   {
571     ret = GNUNET_OK;
572
573     if (req->flags & STATE_OP_FIRST)
574     {
575       ret = db->state_sync_begin (db->cls, &req->channel_key);
576     }
577     if (ret != GNUNET_OK)
578     {
579       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
580                   _("Failed to begin synchronizing state!\n"));
581     }
582     else
583     {
584       ret = db->state_sync_set (db->cls, &req->channel_key, name,
585                                 name + ntohs (req->name_size),
586                                 ntohs (req->header.size) - sizeof (*req)
587                                 - ntohs (req->name_size));
588     }
589
590     if (GNUNET_OK == ret && req->flags & STATE_OP_LAST)
591     {
592       ret = db->state_sync_end (db->cls, &req->channel_key,
593                                 GNUNET_ntohll (req->message_id));
594       if (ret != GNUNET_OK)
595         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
596                     _("Failed to end synchronizing state!\n"));
597     }
598   }
599   send_result_code (client, req->op_id, ret, NULL);
600   GNUNET_SERVER_receive_done (client, GNUNET_OK);
601 }
602
603
604 static void
605 handle_state_reset (void *cls,
606                     struct GNUNET_SERVER_Client *client,
607                     const struct GNUNET_MessageHeader *msg)
608 {
609   const struct OperationRequest *req =
610     (const struct OperationRequest *) msg;
611
612   int ret = db->state_reset (db->cls, &req->channel_key);
613
614   if (ret != GNUNET_OK)
615     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
616                 _("Failed to reset state!\n"));
617
618   send_result_code (client, req->op_id, ret, NULL);
619   GNUNET_SERVER_receive_done (client, GNUNET_OK);
620 }
621
622
623 static void
624 handle_state_hash_update (void *cls,
625                           struct GNUNET_SERVER_Client *client,
626                           const struct GNUNET_MessageHeader *msg)
627 {
628   const struct OperationRequest *req =
629     (const struct OperationRequest *) msg;
630
631   int ret = db->state_reset (db->cls, &req->channel_key);
632
633   if (ret != GNUNET_OK)
634     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
635                 _("Failed to reset state!\n"));
636
637   send_result_code (client, req->op_id, ret, NULL);
638   GNUNET_SERVER_receive_done (client, GNUNET_OK);
639 }
640
641
642 static void
643 handle_state_get (void *cls,
644                   struct GNUNET_SERVER_Client *client,
645                   const struct GNUNET_MessageHeader *msg)
646 {
647   const struct OperationRequest *req =
648     (const struct OperationRequest *) msg;
649
650   struct SendClosure sc = { .op_id = req->op_id, .client = client };
651   int64_t ret = GNUNET_SYSERR;
652   const char *name = (const char *) &req[1];
653   uint16_t name_size = ntohs (req->header.size) - sizeof (*req);
654
655   if (name_size <= 2 || '\0' != name[name_size - 1])
656   {
657     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
658                 _("Tried to get invalid state variable name!\n"));
659     GNUNET_break (0);
660   }
661   else
662   {
663     ret = db->state_get (db->cls, &req->channel_key, name,
664                          &send_state_var, &sc);
665     if (GNUNET_NO == ret && name_size >= 5) /* min: _a_b\0 */
666     {
667       char *p, *n = GNUNET_malloc (name_size);
668       memcpy (n, name, name_size);
669       while (&n[1] < (p = strrchr (n, '_')) && GNUNET_NO == ret)
670       {
671         *p = '\0';
672         ret = db->state_get (db->cls, &req->channel_key, n,
673                              &send_state_var, &sc);
674       }
675       GNUNET_free (n);
676     }
677   }
678   switch (ret)
679   {
680   case GNUNET_OK:
681   case GNUNET_NO:
682     break;
683   default:
684     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
685                 _("Failed to get state variable!\n"));
686   }
687
688   send_result_code (client, req->op_id, ret, NULL);
689   GNUNET_SERVER_receive_done (client, GNUNET_OK);
690 }
691
692
693 static void
694 handle_state_get_prefix (void *cls,
695                          struct GNUNET_SERVER_Client *client,
696                          const struct GNUNET_MessageHeader *msg)
697 {
698   const struct OperationRequest *req =
699     (const struct OperationRequest *) msg;
700
701   struct SendClosure sc = { .op_id = req->op_id, .client = client };
702   int64_t ret = GNUNET_SYSERR;
703   const char *name = (const char *) &req[1];
704   uint16_t name_size = ntohs (req->header.size) - sizeof (*req);
705
706   if (name_size <= 1 || '\0' != name[name_size - 1])
707   {
708     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
709                 _("Tried to get invalid state variable name!\n"));
710     GNUNET_break (0);
711   }
712   else
713   {
714     ret = db->state_get_prefix (db->cls, &req->channel_key, name,
715                                 &send_state_var, &sc);
716   }
717   switch (ret)
718   {
719   case GNUNET_OK:
720   case GNUNET_NO:
721     break;
722   default:
723     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
724                 _("Failed to get state variable!\n"));
725   }
726
727   send_result_code (client, req->op_id, ret, NULL);
728   GNUNET_SERVER_receive_done (client, GNUNET_OK);
729 }
730
731
732 /**
733  * Initialize the PSYCstore service.
734  *
735  * @param cls Closure.
736  * @param server The initialized server.
737  * @param c Configuration to use.
738  */
739 static void
740 run (void *cls, struct GNUNET_SERVER_Handle *server,
741      const struct GNUNET_CONFIGURATION_Handle *c)
742 {
743   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
744     { &handle_membership_store, NULL,
745       GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_STORE,
746       sizeof (struct MembershipStoreRequest) },
747
748     { &handle_membership_test, NULL,
749       GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_TEST,
750       sizeof (struct MembershipTestRequest) },
751
752     { &handle_fragment_store, NULL,
753       GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_STORE, 0, },
754
755     { &handle_fragment_get, NULL,
756       GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET,
757       sizeof (struct FragmentGetRequest) },
758
759     { &handle_message_get, NULL,
760       GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET,
761       sizeof (struct MessageGetRequest) },
762
763     { &handle_message_get_fragment, NULL,
764       GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET_FRAGMENT,
765       sizeof (struct MessageGetFragmentRequest) },
766
767     { &handle_counters_get, NULL,
768       GNUNET_MESSAGE_TYPE_PSYCSTORE_COUNTERS_GET,
769       sizeof (struct OperationRequest) },
770
771     { &handle_state_modify, NULL,
772       GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_MODIFY, 0 },
773
774     { &handle_state_sync, NULL,
775       GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC, 0 },
776
777     { &handle_state_reset, NULL,
778       GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_RESET,
779       sizeof (struct OperationRequest) },
780
781     { &handle_state_hash_update, NULL,
782       GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_HASH_UPDATE,
783       sizeof (struct StateHashUpdateRequest) },
784
785     { &handle_state_get, NULL,
786       GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET, 0 },
787
788     { &handle_state_get_prefix, NULL,
789       GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET_PREFIX, 0 },
790
791     { NULL, NULL, 0, 0 }
792   };
793
794   cfg = c;
795
796   /* Loading database plugin */
797   char *database;
798   if (GNUNET_OK !=
799       GNUNET_CONFIGURATION_get_value_string (cfg, "psycstore", "database",
800                                              &database))
801   {
802     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n");
803   }
804   else
805   {
806     GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_psycstore_%s", database);
807     db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg);
808     GNUNET_free (database);
809   }
810   if (NULL == db)
811   {
812     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
813                 "Could not load database backend `%s'\n",
814                 db_lib_name);
815     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
816     return;
817   }
818
819   stats = GNUNET_STATISTICS_create ("psycstore", cfg);
820   GNUNET_SERVER_add_handlers (server, handlers);
821   nc = GNUNET_SERVER_notification_context_create (server, 1);
822   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
823                                 NULL);
824 }
825
826
827 /**
828  * The main function for the service.
829  *
830  * @param argc number of arguments from the command line
831  * @param argv command line arguments
832  * @return 0 ok, 1 on error
833  */
834 int
835 main (int argc, char *const *argv)
836 {
837   return (GNUNET_OK ==
838           GNUNET_SERVICE_run (argc, argv, "psycstore",
839                               GNUNET_SERVICE_OPTION_NONE,
840                               &run, NULL)) ? 0 : 1;
841 }
842
843
844 /* end of gnunet-service-psycstore.c */