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