changes
[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_Request_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         /* Check compability */
154         if (get_ctx->n->version.abs_value != e->version.abs_value)
155                 return GNUNET_OK;
156
157         get_ctx->get_cb (get_ctx->n, e);
158
159         return GNUNET_OK;
160 }
161
162
163
164
165 void
166 GNUNET_EXPERIMENTATION_experiments_get (struct Node *n,
167                                                                                                                                                                 struct GNUNET_PeerIdentity *issuer,
168                                                                                                                                                                 GNUNET_EXPERIMENTATION_experiments_get_cb get_cb)
169 {
170         struct GetCtx get_ctx;
171
172         GNUNET_assert (NULL != n);
173         GNUNET_assert (NULL != experiments);
174         GNUNET_assert (NULL != get_cb);
175
176         get_ctx.n = n;
177         get_ctx.get_cb = get_cb;
178
179         GNUNET_CONTAINER_multihashmap_get_multiple (experiments,
180                         &issuer->hashPubKey, &get_it, &get_ctx);
181
182         get_cb (n, NULL);
183 }
184
185 /**
186  * Add a new experiment
187  */
188 int GNUNET_EXPERIMENTATION_experiments_add (struct Issuer *i,
189                                                                                                                                                                                 const char *name,
190                                                                                                                                                                                 struct GNUNET_PeerIdentity issuer_id,
191                                                                                                                                                                                 struct GNUNET_TIME_Absolute version,
192                                                                                                                                                                                 char *description,
193                                                                                                                                                                                 uint32_t required_capabilities,
194                                                                                                                                                                                 struct GNUNET_TIME_Absolute start,
195                                                                                                                                                                                 struct GNUNET_TIME_Relative frequency,
196                                                                                                                                                                                 struct GNUNET_TIME_Relative duration,
197                                                                                                                                                                                 struct GNUNET_TIME_Absolute stop)
198 {
199         struct Experiment *e;
200         e = GNUNET_malloc (sizeof (struct Experiment));
201
202         e->name = GNUNET_strdup (name);
203         e->issuer = issuer_id;
204         e->version = version;
205         if (NULL != description)
206                 e->description = GNUNET_strdup (description);
207         e->required_capabilities = required_capabilities;
208         e->start = start;
209         e->frequency = frequency;
210         e->duration = duration;
211         e->stop = stop;
212
213         /* verify experiment */
214         if (GNUNET_SYSERR == experiment_verify (i, e))
215         {
216                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Experiment `%s': Experiment signature is invalid\n"), name);
217                         GNUNET_free (e);
218                         GNUNET_free_non_null (e->name);
219                         GNUNET_free_non_null (e->description);
220                         return GNUNET_SYSERR;
221         }
222
223         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Adding experiment `%s' running from `%s' to `%s' every %llu sec. for %llu sec. \n"),
224                         e->name,
225                         GNUNET_STRINGS_absolute_time_to_string (start),
226                         GNUNET_STRINGS_absolute_time_to_string (stop),
227                         (long long unsigned int) frequency.rel_value / 1000,
228                         (long long unsigned int) duration.rel_value / 1000);
229         GNUNET_CONTAINER_multihashmap_put (experiments, &e->issuer.hashPubKey, e, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
230   GNUNET_STATISTICS_set (GSE_stats, "# experiments", GNUNET_CONTAINER_multihashmap_size (experiments), GNUNET_NO);
231
232         return GNUNET_OK;
233 }
234
235
236 /**
237  * Parse a configuration section containing experiments
238  *
239  * @param cls configuration handle
240  * @param section section name
241  */
242 void exp_file_iterator (void *cls,
243                                                                                                 const char *name)
244 {
245         struct GNUNET_CONFIGURATION_Handle *exp = cls;
246         struct Issuer *i;
247
248         char *val;
249         unsigned long long number;
250
251         /* Experiment values */
252         struct GNUNET_PeerIdentity issuer;
253         struct GNUNET_TIME_Absolute version;
254         char *description;
255         uint32_t required_capabilities;
256         struct GNUNET_TIME_Absolute start ;
257         struct GNUNET_TIME_Absolute stop;
258         struct GNUNET_TIME_Relative frequency;
259         struct GNUNET_TIME_Relative duration;
260
261         GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Parsing section `%s'\n", name);
262
263         /* Mandatory fields */
264
265         /* Issuer */
266         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (exp, name, "ISSUER", &val))
267         {
268                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Experiment `%s': Issuer missing\n"), name);
269                         return;
270         }
271         if (GNUNET_SYSERR == GNUNET_CRYPTO_hash_from_string (val, &issuer.hashPubKey))
272         {
273                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Experiment `%s': Issuer invalid\n"), name);
274                         GNUNET_free (val);
275                         return;
276         }
277         if (NULL == (i = GNUNET_CONTAINER_multihashmap_get (valid_issuers, &issuer.hashPubKey)))
278         {
279                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Experiment `%s': Issuer not accepted!\n"), name);
280                 GNUNET_free (val);
281                 return;
282         }
283         GNUNET_free (val);
284
285         /* Version */
286         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "VERSION", &number))
287         {
288                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Experiment `%s': Version missing or invalid \n"), name);
289                         return;
290         }
291         version.abs_value = number;
292
293         /* Required capabilities */
294         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "CAPABILITIES", &number))
295         {
296                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Experiment `%s': Required capabilities missing \n"), name);
297                         return;
298         }
299         if (number > UINT32_MAX)
300         {
301                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Experiment `%s': Required capabilities invalid \n"), name);
302                 return;
303         }
304         required_capabilities = number;
305
306         /* Optional fields */
307
308         /* Description */
309         GNUNET_CONFIGURATION_get_value_string (exp, name, "DESCRIPTION", &description);
310
311
312
313         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "START", (long long unsigned int *) &start.abs_value))
314                         start = GNUNET_TIME_UNIT_ZERO_ABS;
315
316         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (exp, name, "FREQUENCY", &frequency))
317                         frequency = EXP_DEFAULT_EXP_FREQ;
318         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (exp, name, "DURATION", &duration))
319                         duration = EXP_DEFAULT_EXP_DUR;
320         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (exp, name, "STOP", (long long unsigned int *)&stop.abs_value))
321                         stop = GNUNET_TIME_UNIT_FOREVER_ABS;
322
323         GNUNET_EXPERIMENTATION_experiments_add (i, name, issuer, version,
324                                                                                                                                                                         description, required_capabilities,
325                                                                                                                                                                         start, frequency, duration, stop);
326         GNUNET_free_non_null (description);
327 }
328
329
330 /**
331  * Load experiments from file
332  *
333  * @param file source file
334  */
335 static void
336 load_file (const char * file)
337 {
338         struct GNUNET_CONFIGURATION_Handle *exp = GNUNET_CONFIGURATION_create();
339         if (NULL == exp)
340                 return;
341
342         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_parse (exp, file))
343         {
344                 GNUNET_CONFIGURATION_destroy (exp);
345                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to parse file `%s'\n"), file);
346                 return;
347         }
348         GNUNET_CONFIGURATION_iterate_sections (exp, &exp_file_iterator, exp);
349         GNUNET_CONFIGURATION_destroy (exp);
350 }
351
352
353 /**
354  * Start experiments management
355  */
356 int
357 GNUNET_EXPERIMENTATION_experiments_start ()
358 {
359         struct Issuer *i;
360         char *issuers;
361         char *file;
362         char *pubkey;
363         char *pos;
364         struct GNUNET_PeerIdentity issuer_ID;
365         struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pub;
366         struct GNUNET_HashCode hash;
367
368         /* Load valid issuer */
369         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (GSE_cfg, "EXPERIMENTATION", "ISSUERS", &issuers))
370         {
371                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("No valid experiment issuers configured! Set value to peer id of issuer! Exit...\n"));
372                         return GNUNET_SYSERR;
373         }
374
375         valid_issuers = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
376   for (pos = strtok (issuers, " "); pos != NULL; pos = strtok (NULL, " "))
377   {
378
379                 if (GNUNET_SYSERR == GNUNET_CRYPTO_hash_from_string (pos, &issuer_ID.hashPubKey))
380                 {
381                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Invalid value `%s'\n"), pos);
382                 }
383                 else
384                 {
385                                 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "`%s' is a valid issuer \n", GNUNET_i2s (&issuer_ID));
386                                 i = GNUNET_malloc (sizeof (struct Issuer));
387                                 GNUNET_CONTAINER_multihashmap_put (valid_issuers, &issuer_ID.hashPubKey,
388                                                 i, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
389                                 i = NULL;
390                 }
391   }
392   GNUNET_free (issuers);
393
394   if (0 == GNUNET_CONTAINER_multihashmap_size (valid_issuers))
395   {
396                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("No valid experiment issuers configured! Set value to peer id of issuer! Exit...\n"));
397                 GNUNET_EXPERIMENTATION_experiments_stop ();
398                 return GNUNET_SYSERR;
399   }
400   GNUNET_STATISTICS_set (GSE_stats, "# issuer", GNUNET_CONTAINER_multihashmap_size (valid_issuers), GNUNET_NO);
401
402         if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (GSE_cfg, "EXPERIMENTATION", "PUBKEY", &pubkey))
403         {
404                         if (GNUNET_OK != GNUNET_CRYPTO_ecc_public_key_from_string(pubkey, strlen (pubkey), &pub))
405                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Invalid public key `%s'\n"), pubkey);
406                         else
407                         {
408                                 GNUNET_CRYPTO_hash( &pub, sizeof (pub), &hash);
409                                 if (NULL != (i = GNUNET_CONTAINER_multihashmap_get (valid_issuers, &hash)))
410                                 {
411                                                 i->pubkey = pub;
412                                                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Found issuer for public key `%s'\n"), pubkey);
413                                 }
414                                 else
415                                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("No issuer for public key `%s'\n"), pubkey);
416                         }
417                         GNUNET_free (pubkey);
418         }
419
420         GSE_my_issuer_count = GNUNET_CONTAINER_multihashmap_size (valid_issuers);
421         GSE_my_issuer = GNUNET_malloc (GSE_my_issuer_count * sizeof (struct Experimentation_Request_Issuer));
422         GNUNET_CONTAINER_multihashmap_iterate (valid_issuers, &create_issuer, GSE_my_issuer);
423         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Daemon has %u issuers\n"), GSE_my_issuer_count);
424
425   experiments = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
426   /* Load experiments from file */
427         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (GSE_cfg, "EXPERIMENTATION", "EXPERIMENTS", &file))
428                 return GNUNET_OK;
429
430         if (GNUNET_YES != GNUNET_DISK_file_test (file))
431         {
432                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Cannot read experiments file `%s'\n"), file);
433                 GNUNET_free (file);
434                         return GNUNET_OK;
435         }
436         load_file (file);
437         GNUNET_free (file);
438         return GNUNET_OK;
439 }
440
441
442 /**
443  * Stop experiments management
444  */
445 void
446 GNUNET_EXPERIMENTATION_experiments_stop ()
447 {
448         if (NULL != GSE_my_issuer)
449         {
450                 GNUNET_free (GSE_my_issuer);
451                 GSE_my_issuer = NULL;
452                 GSE_my_issuer_count = 0;
453         }
454         if (NULL != valid_issuers)
455         {
456                 GNUNET_CONTAINER_multihashmap_iterate (valid_issuers, &free_issuer, NULL);
457                 GNUNET_CONTAINER_multihashmap_destroy (valid_issuers);
458         }
459         valid_issuers = NULL;
460         if (NULL != experiments)
461         {
462                 GNUNET_CONTAINER_multihashmap_iterate (experiments, &free_experiment, NULL);
463                 GNUNET_CONTAINER_multihashmap_destroy (experiments);
464         }
465         experiments = NULL;
466 }
467
468 /* end of gnunet-daemon-experimentation_experiments.c */