-just indentation
[oweals/gnunet.git] / src / experimentation / gnunet-daemon-experimentation_experiments.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 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 experimentation/gnunet-daemon-experimentation_experiments.c
23  * @brief experimentation daemon: experiment management
24  * @author Christian Grothoff
25  * @author Matthias Wachs
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_core_service.h"
30 #include "gnunet_statistics_service.h"
31 #include "gnunet-daemon-experimentation.h"
32
33
34 /**
35  * Struct to store information about an experiment issuer
36  */
37 struct Issuer
38 {
39   struct GNUNET_CRYPTO_EccPublicSignKey pubkey;
40 };
41
42
43 /**
44  * Hashmap containing valid experiment issuer
45  */
46 static struct GNUNET_CONTAINER_MultiHashMap *valid_issuers;
47
48 /**
49  * Hashmap containing valid experiments
50  */
51 static struct GNUNET_CONTAINER_MultiHashMap *experiments;
52
53
54 static uint32_t GSE_my_issuer_count;
55
56 /**
57  * Valid experiment issuer for this daemon
58  *
59  * Array Experimentation_Issuer with GSE_my_issuer_count elements
60  */
61 static struct Experimentation_Issuer *GSE_my_issuer;
62
63
64 /**
65  * Verify experiment signature
66  *
67  * @param i issuer
68  * @param e experiment
69  * @return GNUNET_OK or GNUNET_SYSERR
70  */
71 static int
72 experiment_verify (struct Issuer *i, struct Experiment *e)
73 {
74   GNUNET_assert (NULL != i);
75   GNUNET_assert (NULL != e);
76   
77   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
78               "Verification: to be implemented\n");
79   return GNUNET_OK;
80 }
81
82
83 static int 
84 free_experiment (void *cls,
85                  const struct GNUNET_HashCode * key,
86                  void *value)
87 {
88   struct Experiment *e = value;
89
90   GNUNET_break (0 == GNUNET_CONTAINER_multihashmap_remove (experiments, key, value));
91   GNUNET_free_non_null (e->description);
92   GNUNET_free_non_null (e->name);
93   GNUNET_free (e);
94   return GNUNET_OK;
95 }
96
97
98 /**
99  * Free issuer element
100  *
101  * @param cls unused
102  * @param key the key
103  * @param value the issuer element to free
104  * @return GNUNET_OK to continue
105  */
106 static int 
107 free_issuer (void *cls,
108              const struct GNUNET_HashCode * key,
109              void *value)
110 {
111   struct Issuer *i = value;
112
113   GNUNET_break (0 == GNUNET_CONTAINER_multihashmap_remove (valid_issuers, key, value));
114   GNUNET_free (i);
115   return GNUNET_OK;
116 }
117
118
119 static int
120 create_issuer (void *cls,
121                const struct GNUNET_HashCode * key,
122                void *value)
123 {
124   static int i = 0;
125
126   GNUNET_assert (i < GSE_my_issuer_count);
127   GSE_my_issuer[i].issuer_id.hashPubKey = *key;
128   
129   i++;
130   return GNUNET_OK;
131 }
132
133
134
135 /**
136  * Is peer a valid issuer
137  *
138  * @return GNUNET_YES or GNUNET_NO
139  */
140 int
141 GED_experiments_issuer_accepted (struct GNUNET_PeerIdentity *issuer_ID)
142 {
143   if (GNUNET_CONTAINER_multihashmap_contains (valid_issuers, &issuer_ID->hashPubKey))
144     return GNUNET_YES;
145   else
146     return GNUNET_NO;
147 }
148
149
150 struct FindCtx
151 {
152   const char *name;
153   struct GNUNET_TIME_Absolute version;
154   struct Experiment *res;
155 };
156
157
158 static int
159 find_it (void *cls,
160          const struct GNUNET_HashCode * key,
161          void *value)
162 {
163   struct FindCtx *find_ctx = cls;
164   struct Experiment *e = (struct Experiment *) value;
165   
166   if (0 != strcmp(e->name, find_ctx->name))
167     return GNUNET_OK;
168   if (e->version.abs_value_us != find_ctx->version.abs_value_us)
169     return GNUNET_OK;
170   
171   find_ctx->res = e;
172   return GNUNET_NO;
173 }
174
175
176 /*
177  * Find an experiment based on issuer name and version
178  *
179  * @param issuer the issuer
180  * @param name experiment name
181  * @param version experiment version
182  * @return the experiment or NULL if not found
183  */
184 struct Experiment *
185 GED_experiments_find (const struct GNUNET_PeerIdentity *issuer,
186                       const char *name,
187                       const struct GNUNET_TIME_Absolute version)
188 {
189   struct FindCtx find_ctx;
190   
191   find_ctx.name = name;
192   find_ctx.version = version;
193   find_ctx.res = NULL;
194   
195   GNUNET_CONTAINER_multihashmap_get_multiple (experiments,
196                                               &issuer->hashPubKey, 
197                                               &find_it, &find_ctx);
198   return find_ctx.res;
199 }
200
201
202 struct GetCtx
203 {
204   struct Node *n;
205   GNUNET_EXPERIMENTATION_experiments_get_cb get_cb;
206 };
207
208
209 static int
210 get_it (void *cls,
211         const struct GNUNET_HashCode * key,
212         void *value)
213 {
214   struct GetCtx *get_ctx = cls;
215   struct Experiment *e = value;
216
217   get_ctx->get_cb (get_ctx->n, e);
218   
219   return GNUNET_OK;
220 }
221
222
223 void
224 GED_experiments_get (struct Node *n,
225                      struct GNUNET_PeerIdentity *issuer,
226                      GNUNET_EXPERIMENTATION_experiments_get_cb get_cb)
227 {
228   struct GetCtx get_ctx;
229
230   GNUNET_assert (NULL != n);
231   GNUNET_assert (NULL != experiments);
232   GNUNET_assert (NULL != get_cb);
233   
234   get_ctx.n = n;
235   get_ctx.get_cb = get_cb;
236
237   GNUNET_CONTAINER_multihashmap_get_multiple (experiments,
238                                               &issuer->hashPubKey, &get_it, &get_ctx);
239   
240   get_cb (n, NULL);
241 }
242
243
244 /**
245  * Add a new experiment
246  */
247 int
248 GNUNET_EXPERIMENTATION_experiments_add (struct Issuer *i,
249                                         const char *name,
250                                         struct GNUNET_PeerIdentity issuer_id,
251                                         struct GNUNET_TIME_Absolute version,
252                                         char *description,
253                                         uint32_t required_capabilities,
254                                         struct GNUNET_TIME_Absolute start,
255                                         struct GNUNET_TIME_Relative frequency,
256                                         struct GNUNET_TIME_Relative duration,
257                                         struct GNUNET_TIME_Absolute stop)
258 {
259   struct Experiment *e;
260
261   e = GNUNET_new (struct Experiment);  
262   e->name = GNUNET_strdup (name);
263   e->issuer = issuer_id;
264   e->version = version;
265   if (NULL != description)
266     e->description = GNUNET_strdup (description);
267   e->required_capabilities = required_capabilities;
268   e->start = start;
269   e->frequency = frequency;
270   e->duration = duration;
271   e->stop = stop;
272   
273   /* verify experiment */
274   if (GNUNET_SYSERR == experiment_verify (i, e))
275   {
276     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
277                 _("Experiment `%s': Experiment signature is invalid\n"), 
278                 name);
279     GNUNET_free (e);
280     GNUNET_free_non_null (e->name);
281     GNUNET_free_non_null (e->description);
282     return GNUNET_SYSERR;
283   }
284   
285   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
286               _("Adding experiment `%s' running from `%s' to `%s' every %llu sec. for %llu sec. \n"),
287               e->name,
288               GNUNET_STRINGS_absolute_time_to_string (start),
289               GNUNET_STRINGS_absolute_time_to_string (stop),
290               (long long unsigned int) frequency.rel_value_us / 1000000LL,
291               (long long unsigned int) duration.rel_value_us / 1000000LL);
292   GNUNET_CONTAINER_multihashmap_put (experiments,
293                                      &e->issuer.hashPubKey, 
294                                      e, 
295                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
296   GNUNET_STATISTICS_set (GED_stats, 
297                          "# experiments", 
298                          GNUNET_CONTAINER_multihashmap_size (experiments), GNUNET_NO);
299   
300   return GNUNET_OK;
301 }
302
303
304 /**
305  * Parse a configuration section containing experiments
306  *
307  * @param cls configuration handle
308  * @param name section name
309  */
310 static void
311 exp_file_iterator (void *cls,
312                    const char *name)
313 {
314   struct GNUNET_CONFIGURATION_Handle *exp = cls;
315   struct Issuer *i;
316   char *val;
317   unsigned long long number;
318   /* Experiment values */
319   struct GNUNET_PeerIdentity issuer;
320   struct GNUNET_TIME_Absolute version;
321   char *description;
322   uint32_t required_capabilities;
323   struct GNUNET_TIME_Absolute start ;
324   struct GNUNET_TIME_Absolute stop;
325   struct GNUNET_TIME_Relative frequency;
326   struct GNUNET_TIME_Relative duration;
327   
328   /* Mandatory fields */
329   
330   /* Issuer */
331   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (exp, name, "ISSUER", &val))
332   {
333     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
334                 _("Experiment `%s': Issuer missing\n"), name);
335     return;
336   }
337   if (GNUNET_SYSERR == GNUNET_CRYPTO_hash_from_string (val, &issuer.hashPubKey))
338   {
339     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
340                 _("Experiment `%s': Issuer invalid\n"), name);
341     GNUNET_free (val);
342     return;
343   }
344   if (NULL == (i = GNUNET_CONTAINER_multihashmap_get (valid_issuers, &issuer.hashPubKey)))
345   {
346     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
347                 _("Experiment `%s': Issuer not accepted!\n"), name);
348     GNUNET_free (val);
349     return;
350   }
351   GNUNET_free (val);
352   
353   /* Version */
354   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "VERSION", &number))
355   {
356     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
357                 _("Experiment `%s': Version missing or invalid \n"), name);
358     return;
359   }
360   version.abs_value_us = number; // FIXME: what is this supposed to be? Version != TIME!???
361   
362   /* Required capabilities */
363   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "CAPABILITIES", &number))
364   {
365     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
366                 _("Experiment `%s': Required capabilities missing \n"), name);
367     return;
368   }
369   if (number > UINT32_MAX)
370   {
371     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
372                 _("Experiment `%s': Required capabilities invalid \n"), name);
373     return;
374   }
375   required_capabilities = number;
376   
377   /* Optional fields */
378   
379   /* Description */
380   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (exp, name, "DESCRIPTION", &description))
381     description = NULL;
382   
383   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "START", (long long unsigned int *) &start.abs_value_us))
384     start = GNUNET_TIME_UNIT_ZERO_ABS;
385   
386   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (exp, name, "FREQUENCY", &frequency))
387     frequency = EXP_DEFAULT_EXP_FREQ;
388   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (exp, name, "DURATION", &duration))
389     duration = EXP_DEFAULT_EXP_DUR;
390   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "STOP", (long long unsigned int *)&stop.abs_value_us))
391     stop = GNUNET_TIME_UNIT_FOREVER_ABS;
392   
393   GNUNET_EXPERIMENTATION_experiments_add (i, name, issuer, version,
394                                           description, required_capabilities,
395                                           start, frequency, duration, stop);
396   GNUNET_free_non_null (description);
397 }
398
399
400 /**
401  * Load experiments from file
402  *
403  * @param file source file
404  */
405 static void
406 load_file (const char * file)
407 {
408   struct GNUNET_CONFIGURATION_Handle *exp = GNUNET_CONFIGURATION_create();
409
410   if (NULL == exp)
411     return;
412   
413   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_parse (exp, file))
414   {
415     GNUNET_CONFIGURATION_destroy (exp);
416     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
417                 _("Failed to parse file `%s'\n"), 
418                 file);
419     return;
420   }
421   GNUNET_CONFIGURATION_iterate_sections (exp, &exp_file_iterator, exp);
422   GNUNET_CONFIGURATION_destroy (exp);
423 }
424
425
426 /**
427  * Start experiments management
428  */
429 int
430 GED_experiments_start ()
431 {
432   struct Issuer *i;
433   char *issuers;
434   char *file;
435   char *pubkey;
436   char *pos;
437   struct GNUNET_PeerIdentity issuer_ID;
438   struct GNUNET_CRYPTO_EccPublicSignKey pub;
439   struct GNUNET_HashCode hash;
440   
441   /* Load valid issuer */
442   if (GNUNET_SYSERR == 
443       GNUNET_CONFIGURATION_get_value_string (GED_cfg, "EXPERIMENTATION", "ISSUERS", &issuers))
444   {
445     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
446                 _("No valid experiment issuers configured! Set value to peer id of issuer! Exit...\n"));
447     return GNUNET_SYSERR;
448   }
449   
450   valid_issuers = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
451   for (pos = strtok (issuers, " "); pos != NULL; pos = strtok (NULL, " "))
452   {   
453     if (GNUNET_SYSERR == GNUNET_CRYPTO_ecc_public_sign_key_from_string (pos, 
454                                                                         strlen (pos),
455                                                                         &issuer_ID))
456     {
457       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
458                   _("Invalid value `%s'\n"), 
459                   pos);
460     }
461     else
462     {
463       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
464                   "`%s' is a valid issuer \n", 
465                   GNUNET_i2s (&issuer_ID));
466       i = GNUNET_new (struct Issuer);
467       i->pubkey = issuer_ID;
468       GNUNET_CRYPTO_hash( &issuer_ID, sizeof (issuer_ID), &hash);
469       GNUNET_CONTAINER_multihashmap_put (valid_issuers, 
470                                          &hash,
471                                          i, 
472                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
473     }
474   }
475   GNUNET_free (issuers);
476
477   if (0 == GNUNET_CONTAINER_multihashmap_size (valid_issuers))
478   {
479     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
480                 _("No valid experiment issuers configured! Set value to peer id of issuer! Exit...\n"));
481     GED_experiments_stop ();
482     return GNUNET_SYSERR;
483   }
484   GNUNET_STATISTICS_set (GED_stats,
485                          "# issuer", 
486                          GNUNET_CONTAINER_multihashmap_size (valid_issuers), 
487                          GNUNET_NO);
488   if (GNUNET_OK == 
489       GNUNET_CONFIGURATION_get_value_string (GED_cfg, 
490                                              "EXPERIMENTATION",
491                                              "PUBKEY", 
492                                              &pubkey))
493   {
494     if (GNUNET_OK != 
495         GNUNET_CRYPTO_ecc_public_sign_key_from_string (pubkey, strlen (pubkey), &pub))
496     {
497       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
498                   _("Invalid public key `%s'\n"), 
499                   pubkey);
500       GED_experiments_stop ();
501       return GNUNET_SYSERR;
502     }
503     GNUNET_CRYPTO_hash( &pub, sizeof (pub), &hash);
504     if (NULL != (i = GNUNET_CONTAINER_multihashmap_get (valid_issuers, &hash)))
505     {
506       i->pubkey = pub;
507       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Found issuer for public key `%s'\n"), pubkey);
508     }
509     else
510     {
511     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("No issuer for public key `%s'\n"), pubkey);
512     }
513     GNUNET_free (pubkey);
514   }
515
516   GSE_my_issuer_count = GNUNET_CONTAINER_multihashmap_size (valid_issuers);
517   GSE_my_issuer = GNUNET_malloc (GSE_my_issuer_count * sizeof (struct Experimentation_Issuer));
518   GNUNET_CONTAINER_multihashmap_iterate (valid_issuers, &create_issuer, GSE_my_issuer);
519   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
520               "Daemon has %u issuers\n", 
521               GSE_my_issuer_count);
522
523   experiments = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
524   /* Load experiments from file */
525   if (GNUNET_SYSERR ==
526       GNUNET_CONFIGURATION_get_value_string (GED_cfg,
527                                              "EXPERIMENTATION", 
528                                              "EXPERIMENTS",
529                                              &file))
530     return GNUNET_OK;
531   
532   if (GNUNET_YES != GNUNET_DISK_file_test (file))
533   {
534     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
535                 _("Cannot read experiments file `%s'\n"), file);
536     GNUNET_free (file);
537     return GNUNET_OK;
538   }
539   load_file (file);
540   GNUNET_free (file);
541   return GNUNET_OK;
542 }
543
544
545 /**
546  * Stop experiments management
547  */
548 void
549 GED_experiments_stop ()
550 {
551   if (NULL != GSE_my_issuer)
552   {
553     GNUNET_free (GSE_my_issuer);
554     GSE_my_issuer = NULL;
555     GSE_my_issuer_count = 0;
556   }
557   if (NULL != valid_issuers)
558   {
559     GNUNET_CONTAINER_multihashmap_iterate (valid_issuers, &free_issuer, NULL);
560     GNUNET_CONTAINER_multihashmap_destroy (valid_issuers);
561   }
562   valid_issuers = NULL;
563   if (NULL != experiments)
564   {
565     GNUNET_CONTAINER_multihashmap_iterate (experiments, &free_experiment, NULL);
566     GNUNET_CONTAINER_multihashmap_destroy (experiments);
567   }
568   experiments = NULL;
569 }
570
571 /* end of gnunet-daemon-experimentation_experiments.c */