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