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