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