- use ecc for regex profiler daemon
[oweals/gnunet.git] / src / regex / gnunet-daemon-regexprofiler.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012, 2013 Christian Grothoff
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 regex/gnunet-daemon-regexprofiler.c
23  * @brief daemon that uses mesh to announce a regular expression. Used in
24  * conjunction with gnunet-regex-profiler to announce regexes on serveral peers
25  * without the need to explicitly connect to the mesh service running on the
26  * peer from within the profiler.
27  * @author Maximilian Szengel
28  * @author Bartlomiej Polot
29  */
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32 #include "gnunet_regex_lib.h"
33 #include "gnunet_dht_service.h"
34 #include "gnunet_statistics_service.h"
35
36 /**
37  * Return value from 'main'.
38  */
39 static int global_ret;
40
41 /**
42  * Configuration we use.
43  */
44 static const struct GNUNET_CONFIGURATION_Handle *cfg;
45
46 /**
47  * Handle to the statistics service.
48  */
49 static struct GNUNET_STATISTICS_Handle *stats_handle;
50
51 /**
52  * Peer's dht handle.
53  */
54 static struct GNUNET_DHT_Handle *dht_handle;
55
56 /**
57  * Peer's regex announce handle.
58  */
59 static struct GNUNET_REGEX_announce_handle *announce_handle;
60
61 /**
62  * Hostkey generation context
63  */
64 static struct GNUNET_CRYPTO_EccKeyGenerationContext *keygen;
65
66 /**
67  * Periodically reannounce regex.
68  */
69 static GNUNET_SCHEDULER_TaskIdentifier reannounce_task;
70
71 /**
72  * How often reannounce regex.
73  */
74 static struct GNUNET_TIME_Relative reannounce_freq;
75
76 /**
77  * Random delay to spread out load on the DHT.
78  */
79 static struct GNUNET_TIME_Relative announce_delay;
80
81 /**
82  * Local peer's PeerID.
83  */
84 static struct GNUNET_PeerIdentity my_full_id;
85
86 /**
87  * Maximal path compression length for regex announcing.
88  */
89 static unsigned long long max_path_compression;
90
91 /**
92  * Name of the file containing policies that this peer should announce. One
93  * policy per line.
94  */
95 static char * policy_filename;
96
97 /**
98  * Prefix to add before every regex we're announcing.
99  */
100 static char * regex_prefix;
101
102 /**
103  * Regex with prefix.
104  */
105 static char *rx_with_pfx;
106
107
108 /**
109  * Task run during shutdown.
110  *
111  * @param cls unused
112  * @param tc unused
113  */
114 static void
115 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
116 {
117   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutting down\n");
118
119   if (NULL != keygen)
120   {
121     GNUNET_CRYPTO_ecc_key_create_stop (keygen);
122     keygen = NULL;
123   }
124   if (NULL != announce_handle)
125   {
126     GNUNET_REGEX_announce_cancel (announce_handle);
127     announce_handle = NULL;
128   }
129
130   if (NULL != dht_handle)
131   {
132     GNUNET_DHT_disconnect (dht_handle);
133     dht_handle = NULL;
134   }
135
136   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shut down\n");
137 }
138
139
140 /**
141  * Announce a previously announced regex re-using cached data.
142  * 
143  * @param cls Closure (regex to announce if needed).
144  * @param tc TaskContext.
145  */
146 static void
147 reannounce_regex (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
148 {
149   char *regex = cls;
150   reannounce_task = GNUNET_SCHEDULER_NO_TASK;
151   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
152   {
153     GNUNET_free (regex);
154     return;
155   }
156
157   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Announcing regex: %s\n", regex);
158   GNUNET_STATISTICS_update (stats_handle, "# regexes announced", 1, GNUNET_NO);
159   if (NULL == announce_handle && NULL != regex)
160   {
161     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
162                 "First time, creating regex: %s\n",
163                 regex);
164     announce_handle = GNUNET_REGEX_announce (dht_handle,
165                                             &my_full_id,
166                                             regex,
167                                             (unsigned int) max_path_compression,
168                                             stats_handle);
169   }
170   else
171   {
172     GNUNET_assert (NULL != announce_handle);
173     GNUNET_REGEX_reannounce (announce_handle);
174   }
175
176   reannounce_task = 
177     GNUNET_SCHEDULER_add_delayed (
178       GNUNET_TIME_relative_add (reannounce_freq,
179                                 GNUNET_TIME_relative_multiply (
180                                   GNUNET_TIME_UNIT_SECONDS,
181                                   GNUNET_CRYPTO_random_u32 (
182                                     GNUNET_CRYPTO_QUALITY_WEAK,
183                                     600))),
184       &reannounce_regex,
185       cls);
186 }
187
188
189 /**
190  * Announce the given regular expression using Mesh and the path compression
191  * length read from config.
192  *
193  * @param regex regular expression to announce on this peer's mesh.
194  */
195 static void
196 announce_regex (const char * regex)
197 {
198   char *copy;
199
200   if (NULL == regex || 0 == strlen (regex))
201   {
202     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot announce empty regex\n");
203     return;
204   }
205
206   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == reannounce_task);
207   copy = GNUNET_strdup (regex);
208   reannounce_task = GNUNET_SCHEDULER_add_delayed (announce_delay,
209                                                   reannounce_regex,
210                                                   (void *) copy);
211 }
212
213
214 /**
215  * Load regular expressions from filename into 'rxes' array. Array needs to be freed.
216  *
217  * @param filename filename of the file containing the regexes, one per line.
218  * @param rx string with the union of all regular expressions.
219  *
220  * @return number of regular expressions read from filename and in rxes array.
221  * FIXME use load regex lib function
222  */
223 static unsigned int
224 load_regexes (const char *filename, char **rx)
225 {
226   char *data;
227   char *buf;
228   uint64_t filesize;
229   unsigned int offset;
230   unsigned int rx_cnt;
231
232   if (GNUNET_YES != GNUNET_DISK_file_test (policy_filename))
233   {
234     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
235                 "Could not find policy file %s\n", policy_filename);
236     return 0;
237   }
238   if (GNUNET_OK != GNUNET_DISK_file_size (policy_filename, &filesize, GNUNET_YES, GNUNET_YES))
239     filesize = 0;
240   if (0 == filesize)
241   {
242     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Policy file %s is empty.\n", policy_filename);
243     return 0;
244   }
245   data = GNUNET_malloc (filesize);
246   if (filesize != GNUNET_DISK_fn_read (policy_filename, data, filesize))
247   {
248     GNUNET_free (data);
249     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not read policy file %s.\n",
250                 policy_filename);
251     return 0;
252   }
253   buf = data;
254   offset = 0;
255   rx_cnt = 0;
256   while (offset < (filesize - 1))
257   {
258     offset++;
259     if ((data[offset] == '\n') && (buf != &data[offset]))
260     {
261       data[offset] = '|';
262       buf = &data[offset + 1];
263       rx_cnt++;
264     }
265     else if ((data[offset] == '\n') || (data[offset] == '\0'))
266       buf = &data[offset + 1];
267   }
268   data[offset] = '\0';
269   *rx = data;
270
271   return rx_cnt;
272 }
273
274
275 /**
276  * Callback for hostkey read/generation
277  *
278  * @param cls Closure (not used).
279  * @param pk The private key of the local peer.
280  * @param emsg Error message if applicable.
281  */
282 static void
283 key_generation_cb (void *cls,
284                    struct GNUNET_CRYPTO_EccPrivateKey *pk,
285                    const char *emsg)
286 {
287   struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
288
289   keygen = NULL;
290   if (NULL == pk)
291   {
292     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
293                 _("Regexprofiler could not access hostkey: %s. Exiting.\n"),
294                 emsg);
295     GNUNET_SCHEDULER_shutdown ();
296     return;
297   }
298
299   GNUNET_CRYPTO_ecc_key_get_public (pk, &my_public_key);
300   GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
301                       &my_full_id.hashPubKey);
302
303   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
304               "Regexprofiler for peer [%s] starting\n",
305               GNUNET_i2s(&my_full_id));
306   announce_regex (rx_with_pfx);
307   GNUNET_free (rx_with_pfx);
308 }
309
310
311 /**
312  * @brief Main function that will be run by the scheduler.
313  *
314  * @param cls closure
315  * @param args remaining command-line arguments
316  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
317  * @param cfg_ configuration
318  */
319 static void
320 run (void *cls, char *const *args GNUNET_UNUSED,
321      const char *cfgfile GNUNET_UNUSED,
322      const struct GNUNET_CONFIGURATION_Handle *cfg_)
323 {
324   char *regex = NULL;
325   char *keyfile;
326
327   cfg = cfg_;
328
329   if (GNUNET_OK !=
330       GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
331                                                &keyfile))
332   {
333     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
334                 _
335                 ("%s service is lacking key configuration settings (%s).  Exiting.\n"),
336                 "regexprofiler", "hostkey");
337     GNUNET_SCHEDULER_shutdown ();
338     return;
339   }
340
341   if (GNUNET_OK !=
342       GNUNET_CONFIGURATION_get_value_number (cfg, "REGEXPROFILER", "MAX_PATH_COMPRESSION",
343                                              &max_path_compression))
344   {
345     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
346                 _
347                 ("%s service is lacking key configuration settings (%s).  Exiting.\n"),
348                 "regexprofiler", "max_path_compression");
349     global_ret = GNUNET_SYSERR;
350     GNUNET_SCHEDULER_shutdown ();
351     return;
352   }
353
354   if (GNUNET_OK !=
355       GNUNET_CONFIGURATION_get_value_filename (cfg, "REGEXPROFILER",
356                                                "POLICY_FILE", &policy_filename))
357   {
358     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
359                 _
360                 ("%s service is lacking key configuration settings (%s).  Exiting.\n"),
361                 "regexprofiler", "policy_file");
362     global_ret = GNUNET_SYSERR;
363     GNUNET_SCHEDULER_shutdown ();
364     return;
365   }
366
367   if (GNUNET_OK !=
368       GNUNET_CONFIGURATION_get_value_string (cfg, "REGEXPROFILER",
369                                              "REGEX_PREFIX", &regex_prefix))
370   {
371     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
372                 _
373                 ("%s service is lacking key configuration settings (%s).  Exiting.\n"),
374                 "regexprofiler", "regex_prefix");
375     global_ret = GNUNET_SYSERR;
376     GNUNET_SCHEDULER_shutdown ();
377     return;
378   }
379
380   if (GNUNET_OK !=
381       GNUNET_CONFIGURATION_get_value_time (cfg, "REGEXPROFILER",
382                                            "REANNOUNCE_FREQ", &reannounce_freq))
383   {
384     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
385                 "reannounce_freq not given. Using 10 minutes.\n");
386     reannounce_freq =
387       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 10);
388
389   }
390     announce_delay =
391     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
392                                    GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 600));
393
394   stats_handle = GNUNET_STATISTICS_create ("regexprofiler", cfg);
395
396   dht_handle = GNUNET_DHT_connect (cfg, 1);
397
398   if (NULL == dht_handle)
399   {
400     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
401                 "Could not acquire dht handle. Exiting.\n");
402     global_ret = GNUNET_SYSERR;
403     GNUNET_SCHEDULER_shutdown ();
404     return;
405   }
406
407   /* Read regexes from policy files */
408   if (0 == load_regexes (policy_filename, &regex))
409   {
410     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
411                 "Policy file %s contains no policies. Exiting.\n",
412                 policy_filename);
413     global_ret = GNUNET_SYSERR;
414     GNUNET_SCHEDULER_shutdown ();
415     return;
416   }
417
418   /* Announcing regexes from policy_filename */
419   GNUNET_asprintf (&rx_with_pfx, "%s(%s)", regex_prefix, regex);
420   GNUNET_free (regex);
421
422   keygen = GNUNET_CRYPTO_ecc_key_create_start (keyfile,
423                                                &key_generation_cb,
424                                                NULL);
425   GNUNET_free (keyfile);
426
427   /* Scheduled the task to clean up when shutdown is called */
428   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
429                                 NULL);
430 }
431
432
433 /**
434  * The main function of the regexprofiler service.
435  *
436  * @param argc number of arguments from the command line
437  * @param argv command line arguments
438  * @return 0 ok, 1 on error
439  */
440 int
441 main (int argc, char *const *argv)
442 {
443   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
444     GNUNET_GETOPT_OPTION_END
445   };
446
447   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
448     return 2;
449   return (GNUNET_OK ==
450           GNUNET_PROGRAM_run (argc, argv, "regexprofiler",
451                               gettext_noop
452                               ("Daemon to announce regular expressions for the peer using mesh."),
453                               options, &run, NULL)) ? global_ret : 1;
454 }
455
456
457 #ifdef LINUX
458 #include <malloc.h>
459
460 /**
461  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
462  */
463 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
464 {
465   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
466   mallopt (M_TOP_PAD, 1 * 1024);
467   malloc_trim (0);
468 }
469 #endif
470
471
472 /* end of gnunet-daemon-regexprofiler.c */