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