- remove adjust
[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 (const struct GNUNET_CRYPTO_EddsaPublicKey *issuer_id)
109 {
110   struct GNUNET_HashCode hash;
111
112   GNUNET_CRYPTO_hash (issuer_id, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), &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_experiment_key (const struct GNUNET_CRYPTO_EddsaPublicKey *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_CRYPTO_EddsaPublicKey),
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_CRYPTO_EddsaPublicKey *issuer,
148                       const char *name,
149                       const struct GNUNET_TIME_Absolute version)
150 {
151   struct GNUNET_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_EddsaPublicKey *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_EddsaPublicKey)))
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_EddsaPublicKey *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_EddsaPublicKey *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   struct GNUNET_HashCode hc;
224
225   e = GNUNET_new (struct Experiment);
226   e->name = GNUNET_strdup (name);
227   e->issuer = *issuer_id;
228   e->version = version;
229   if (NULL != description)
230     e->description = GNUNET_strdup (description);
231   e->required_capabilities = required_capabilities;
232   e->start = start;
233   e->frequency = frequency;
234   e->duration = duration;
235   e->stop = stop;
236
237   /* verify experiment */
238   if (GNUNET_SYSERR == experiment_verify (i, e))
239   {
240     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
241                 _("Experiment `%s': Experiment signature is invalid\n"),
242                 name);
243     GNUNET_free (e);
244     GNUNET_free_non_null (e->name);
245     GNUNET_free_non_null (e->description);
246     return GNUNET_SYSERR;
247   }
248
249   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
250               _("Adding experiment `%s' running from `%s' to `%s' every %llu sec. for %llu sec. \n"),
251               e->name,
252               GNUNET_STRINGS_absolute_time_to_string (start),
253               GNUNET_STRINGS_absolute_time_to_string (stop),
254               (long long unsigned int) frequency.rel_value_us / 1000000LL,
255               (long long unsigned int) duration.rel_value_us / 1000000LL);
256   get_experiment_key (&e->issuer,
257                       name,
258                       version,
259                       &hc);
260   GNUNET_CONTAINER_multihashmap_put (experiments,
261                                      &hc,
262                                      e,
263                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
264   GNUNET_STATISTICS_set (GED_stats,
265                          "# experiments",
266                          GNUNET_CONTAINER_multihashmap_size (experiments), GNUNET_NO);
267
268   return GNUNET_OK;
269 }
270
271
272 /**
273  * Parse a configuration section containing experiments
274  *
275  * @param cls configuration handle
276  * @param name section name
277  */
278 static void
279 exp_file_iterator (void *cls,
280                    const char *name)
281 {
282   struct GNUNET_CONFIGURATION_Handle *exp = cls;
283   struct Issuer *i;
284   char *val;
285   unsigned long long number;
286   /* Experiment values */
287   struct GNUNET_CRYPTO_EddsaPublicKey issuer;
288   struct GNUNET_TIME_Absolute version;
289   char *description;
290   uint32_t required_capabilities;
291   struct GNUNET_TIME_Absolute start ;
292   struct GNUNET_TIME_Absolute stop;
293   struct GNUNET_TIME_Relative frequency;
294   struct GNUNET_TIME_Relative duration;
295   struct GNUNET_HashCode phash;
296
297   /* Mandatory fields */
298
299   /* Issuer */
300   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (exp, name, "ISSUER", &val))
301   {
302     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
303                 _("Experiment `%s': Issuer missing\n"), name);
304     return;
305   }
306   if (GNUNET_SYSERR ==
307       GNUNET_CRYPTO_eddsa_public_key_from_string (val,
308                                                      strlen (val),
309                                                      &issuer))
310   {
311     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
312                 _("Experiment `%s': Issuer invalid\n"), name);
313     GNUNET_free (val);
314     return;
315   }
316   GNUNET_CRYPTO_hash (&issuer, sizeof (issuer), &phash);
317   if (NULL == (i = GNUNET_CONTAINER_multihashmap_get (valid_issuers, &phash)))
318   {
319     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
320                 _("Experiment `%s': Issuer not accepted!\n"), name);
321     GNUNET_free (val);
322     return;
323   }
324   GNUNET_free (val);
325
326   /* Version */
327   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "VERSION", &number))
328   {
329     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
330                 _("Experiment `%s': Version missing or invalid \n"), name);
331     return;
332   }
333   version.abs_value_us = number; // FIXME: what is this supposed to be? Version != TIME!???
334
335   /* Required capabilities */
336   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "CAPABILITIES", &number))
337   {
338     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
339                 _("Experiment `%s': Required capabilities missing \n"), name);
340     return;
341   }
342   if (number > UINT32_MAX)
343   {
344     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
345                 _("Experiment `%s': Required capabilities invalid \n"), name);
346     return;
347   }
348   required_capabilities = number;
349
350   /* Optional fields */
351
352   /* Description */
353   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (exp, name, "DESCRIPTION", &description))
354     description = NULL;
355
356   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "START", (long long unsigned int *) &start.abs_value_us))
357     start = GNUNET_TIME_UNIT_ZERO_ABS;
358
359   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (exp, name, "FREQUENCY", &frequency))
360     frequency = EXP_DEFAULT_EXP_FREQ;
361   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (exp, name, "DURATION", &duration))
362     duration = EXP_DEFAULT_EXP_DUR;
363   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "STOP", (long long unsigned int *)&stop.abs_value_us))
364     stop = GNUNET_TIME_UNIT_FOREVER_ABS;
365
366   GNUNET_EXPERIMENTATION_experiments_add (i, name, &issuer, version,
367                                           description, required_capabilities,
368                                           start, frequency, duration, stop);
369   GNUNET_free_non_null (description);
370 }
371
372
373 /**
374  * Load experiments from file
375  *
376  * @param file source file
377  */
378 static void
379 load_file (const char * file)
380 {
381   struct GNUNET_CONFIGURATION_Handle *exp = GNUNET_CONFIGURATION_create();
382
383   if (NULL == exp)
384     return;
385
386   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_parse (exp, file))
387   {
388     GNUNET_CONFIGURATION_destroy (exp);
389     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
390                 _("Failed to parse file `%s'\n"),
391                 file);
392     return;
393   }
394   GNUNET_CONFIGURATION_iterate_sections (exp, &exp_file_iterator, exp);
395   GNUNET_CONFIGURATION_destroy (exp);
396 }
397
398
399 /**
400  * Start experiments management
401  */
402 int
403 GED_experiments_start ()
404 {
405   struct Issuer *i;
406   char *issuers;
407   char *file;
408   char *pos;
409   struct GNUNET_CRYPTO_EddsaPublicKey issuer_ID;
410   struct GNUNET_HashCode hash;
411
412   /* Load valid issuer */
413   if (GNUNET_SYSERR ==
414       GNUNET_CONFIGURATION_get_value_string (GED_cfg,
415                                              "EXPERIMENTATION",
416                                              "ISSUERS",
417                                              &issuers))
418   {
419     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
420                 _("No valid experiment issuers configured! Set value to public keys of issuers! Exiting.\n"));
421     GED_experiments_stop ();
422     return GNUNET_SYSERR;
423   }
424
425   valid_issuers = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
426   for (pos = strtok (issuers, " "); pos != NULL; pos = strtok (NULL, " "))
427   {
428     if (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_public_key_from_string (pos,
429                                                                         strlen (pos),
430                                                                         &issuer_ID))
431     {
432       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
433                                  "EXPERIMENTATION",
434                                  "ISSUERS",
435                                  _("Invalid value for public key\n"));
436       GED_experiments_stop ();
437       GNUNET_free (issuers);
438       return GNUNET_SYSERR;
439     }
440     i = GNUNET_new (struct Issuer);
441     i->pubkey = issuer_ID;
442     GNUNET_CRYPTO_hash( &issuer_ID, sizeof (issuer_ID), &hash);
443     GNUNET_CONTAINER_multihashmap_put (valid_issuers,
444                                        &hash,
445                                        i,
446                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
447   }
448   GNUNET_free (issuers);
449   if (0 == GNUNET_CONTAINER_multihashmap_size (valid_issuers))
450   {
451     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
452                 _("No valid experiment issuers configured! Set value to public keys of issuers! Exiting.\n"));
453     GED_experiments_stop ();
454     return GNUNET_SYSERR;
455   }
456   GNUNET_STATISTICS_set (GED_stats,
457                          "# issuer",
458                          GNUNET_CONTAINER_multihashmap_size (valid_issuers),
459                          GNUNET_NO);
460
461   experiments = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
462   /* Load experiments from file */
463   if (GNUNET_SYSERR ==
464       GNUNET_CONFIGURATION_get_value_string (GED_cfg,
465                                              "EXPERIMENTATION",
466                                              "EXPERIMENTS",
467                                              &file))
468     return GNUNET_OK;
469
470   if (GNUNET_YES != GNUNET_DISK_file_test (file))
471   {
472     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
473                 _("Cannot read experiments file `%s'\n"), file);
474     GNUNET_free (file);
475     return GNUNET_OK;
476   }
477   load_file (file);
478   GNUNET_free (file);
479   return GNUNET_OK;
480 }
481
482
483 /**
484  * Stop experiments management
485  */
486 void
487 GED_experiments_stop ()
488 {
489   if (NULL != valid_issuers)
490   {
491     GNUNET_CONTAINER_multihashmap_iterate (valid_issuers, &free_issuer, NULL);
492     GNUNET_CONTAINER_multihashmap_destroy (valid_issuers);
493   }
494   valid_issuers = NULL;
495   if (NULL != experiments)
496   {
497     GNUNET_CONTAINER_multihashmap_iterate (experiments, &free_experiment, NULL);
498     GNUNET_CONTAINER_multihashmap_destroy (experiments);
499   }
500   experiments = NULL;
501 }
502
503 /* end of gnunet-daemon-experimentation_experiments.c */