sensor: towards profiler
[oweals/gnunet.git] / src / sensor / gnunet-sensor-profiler.c
1 /*
2      This file is part of GNUnet.
3      (C)
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  * TODO:
23  * - Run X peers
24  * - Rewrite interval time (optional)
25  * - Run 1 dashboard
26  * - Monitor dashboard records
27  * - Prompt for anomalies when ready:
28  *  -- Cut Y peers (remove their connections to other X-Y peers but not the connections among themselves)
29  */
30
31 /**
32  * @file sensor/gnunet-sensor-profiler.c
33  * @brief Profiler for the sensor service
34  * @author Omar Tarabai
35  */
36 #include "platform.h"
37 #include "gnunet_util_lib.h"
38 #include "gnunet_testbed_service.h"
39
40 /**
41  * Information about a single peer
42  */
43 struct PeerInfo
44 {
45
46   /**
47    * Peer Identity
48    */
49   struct GNUNET_PeerIdentity peer_id;
50
51   /**
52    * Testbed peer handle
53    */
54   struct GNUNET_TESTBED_Peer *testbed_peer;
55
56 };
57
58
59 /**
60  * Name of the configuration file used
61  */
62 static const char *cfg_filename = "gnunet-sensor-profiler.conf";
63
64 /**
65  * Directory to read sensor definitions from
66  */
67 static const char *sensor_src_dir = "sensors";
68
69 /**
70  * Directory to write new sensor definitions to
71  */
72 static const char *sensor_dst_dir = "/tmp/gnunet-sensor-profiler";
73
74 /**
75  * Return value of the program
76  */
77 static int ok = 1;
78
79 /**
80  * Number of peers to run (Option -p)
81  */
82 static unsigned int num_peers = 0;
83
84 /**
85  * Set sensors running interval to this value (Option -i)
86  */
87 static unsigned int sensors_interval = 0;
88
89 /**
90  * Array of peer info for all peers
91  */
92 static struct PeerInfo *all_peers_info;
93
94 /**
95  * Number of peers that we already collected and start their info
96  */
97 static int peers_known = 0;
98
99
100 /**
101  * Copy directory recursively
102  *
103  * @param src Path to source directory
104  * @param dst Destination directory, will be created if it does not exist
105  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
106  */
107 static int
108 copy_dir (const char *src, const char *dst);
109
110
111 /**
112  * Do clean up and shutdown scheduler
113  */
114 static void
115 do_shutdown ()                  // TODO: schedule timeout shutdown
116 {
117   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down.\n");
118   if (NULL != all_peers_info)
119   {
120     GNUNET_free (all_peers_info);
121     all_peers_info = NULL;
122   }
123   GNUNET_SCHEDULER_shutdown ();
124 }
125
126
127 /**
128  * Function called with each file/folder inside a directory that is being copied.
129  *
130  * @param cls closure, destination directory
131  * @param filename complete filename (absolute path)
132  * @return #GNUNET_OK to continue to iterate.
133  *         #GNUNET_SYSERR to abort iteration with error
134  */
135 static int
136 copy_dir_scanner (void *cls, const char *filename)
137 {
138   char *dst_dir = cls;
139   char *dst;
140   int copy_result;
141
142   GNUNET_asprintf (&dst, "%s%s%s", dst_dir, DIR_SEPARATOR_STR,
143                    GNUNET_STRINGS_get_short_name (filename));
144   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Copying `%s' to `%s'.\n", filename,
145               dst);
146   if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_YES))
147     copy_result = copy_dir (filename, dst);
148   else
149   {
150     if (GNUNET_YES == GNUNET_DISK_file_test (dst))
151       GNUNET_DISK_directory_remove (dst);
152     copy_result = GNUNET_DISK_file_copy (filename, dst);
153   }
154   GNUNET_free (dst);
155   return copy_result;
156 }
157
158
159 /**
160  * Copy directory recursively
161  *
162  * @param src Path to source directory
163  * @param dst Destination directory, will be created if it does not exist
164  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
165  */
166 static int
167 copy_dir (const char *src, const char *dst)
168 {
169   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Copying directory `%s' to `%s'.\n", src,
170               dst);
171   if (GNUNET_YES != GNUNET_DISK_directory_test (src, GNUNET_YES))
172     return GNUNET_SYSERR;
173   if (GNUNET_OK != GNUNET_DISK_directory_create (dst))
174     return GNUNET_SYSERR;
175   if (GNUNET_SYSERR ==
176       GNUNET_DISK_directory_scan (src, &copy_dir_scanner, (char *) dst))
177     return GNUNET_SYSERR;
178   return GNUNET_OK;
179 }
180
181
182 /**
183  * Function called with each file/folder inside source sensor directory.
184  *
185  * @param cls closure (unused)
186  * @param filename complete filename (absolute path)
187  * @return #GNUNET_OK to continue to iterate.
188  */
189 static int
190 sensor_dir_scanner (void *cls, const char *filename)
191 {
192   const char *file_basename;
193   char *dst_path;
194   struct GNUNET_CONFIGURATION_Handle *sensor_cfg;
195
196   file_basename = GNUNET_STRINGS_get_short_name (filename);
197   GNUNET_asprintf (&dst_path, "%s%s%s", sensor_dst_dir, DIR_SEPARATOR_STR,
198                    file_basename);
199   if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_NO))
200   {
201     GNUNET_assert (GNUNET_OK == copy_dir (filename, dst_path));
202   }
203   else
204   {
205     sensor_cfg = GNUNET_CONFIGURATION_create ();
206     GNUNET_assert (GNUNET_OK ==
207                    GNUNET_CONFIGURATION_parse (sensor_cfg, filename));
208     GNUNET_CONFIGURATION_set_value_string (sensor_cfg, file_basename,
209                                            "COLLECTION_POINT",
210                                            GNUNET_i2s_full (&all_peers_info[0].
211                                                             peer_id));
212     if (sensors_interval > 0)
213     {
214       GNUNET_CONFIGURATION_set_value_number (sensor_cfg, file_basename,
215                                              "INTERVAL",
216                                              (unsigned long long int)
217                                              sensors_interval);
218     }
219     GNUNET_CONFIGURATION_write (sensor_cfg, dst_path);
220     GNUNET_CONFIGURATION_destroy (sensor_cfg);
221   }
222   GNUNET_free (dst_path);
223   return GNUNET_OK;
224 }
225
226
227 /**
228  * Load sensor definitions and rewrite them to tmp location.
229  * Add collection point peer id and change running interval if needed.
230  */
231 static void
232 rewrite_sensors ()
233 {
234   GNUNET_assert (GNUNET_YES ==
235                  GNUNET_DISK_directory_test (sensor_src_dir, GNUNET_YES));
236   GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_create (sensor_dst_dir));
237   GNUNET_DISK_directory_scan (sensor_src_dir, &sensor_dir_scanner, NULL);
238 }
239
240
241 /**
242  * Callback to be called when dashboard service is started
243  *
244  * @param cls the callback closure from functions generating an operation
245  * @param op the operation that has been finished
246  * @param emsg error message in case the operation has failed; will be NULL if
247  *          operation has executed successfully.
248  */
249 static void
250 dashboard_started (void *cls, struct GNUNET_TESTBED_Operation *op,
251                    const char *emsg)
252 {
253   if (NULL != emsg)
254   {
255     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ERROR: %s.\n", emsg);
256     GNUNET_assert (0);
257   }
258   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dashboard service started.\n");
259   //TODO:
260   GNUNET_TESTBED_operation_done (op);
261 }
262
263
264 /**
265  * Callback to be called when the requested peer information is available
266  *
267  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
268  * @param op the operation this callback corresponds to
269  * @param pinfo the result; will be NULL if the operation has failed
270  * @param emsg error message if the operation has failed; will be NULL if the
271  *          operation is successfull
272  */
273 static void
274 peer_info_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op,
275               const struct GNUNET_TESTBED_PeerInformation *pinfo,
276               const char *emsg)
277 {
278   struct GNUNET_TESTBED_Peer *testbed_peer = cb_cls;
279   struct PeerInfo *peer = &all_peers_info[peers_known];
280
281   peer->testbed_peer = testbed_peer;
282   GNUNET_CRYPTO_get_peer_identity (pinfo->result.cfg, &peer->peer_id);
283   if (0 == peers_known)         /* First peer is collection point */
284   {
285     /* Rewrite sensors */
286     rewrite_sensors ();
287     /* Start dashboard */
288     GNUNET_TESTBED_peer_manage_service (NULL, testbed_peer, "sensordashboard",
289                                         &dashboard_started, NULL, 1);
290   }
291   peers_known++;
292   GNUNET_TESTBED_operation_done (op);
293 }
294
295
296 /**
297  * Signature of a main function for a testcase.
298  *
299  * @param cls closure
300  * @param h the run handle
301  * @param num number of peers in 'peers'
302  * @param peers handle to peers run in the testbed.  NULL upon timeout (see
303  *          GNUNET_TESTBED_test_run()).
304  * @param links_succeeded the number of overlay link connection attempts that
305  *          succeeded
306  * @param links_failed the number of overlay link connection attempts that
307  *          failed
308  * @see GNUNET_TESTBED_test_run()
309  */
310 static void
311 test_master (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num,
312              struct GNUNET_TESTBED_Peer **peers, unsigned int links_succeeded,
313              unsigned int links_failed)
314 {
315   int i;
316
317   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
318               "%d peers started. %d links succeeded. %d links failed.\n",
319               num_peers, links_succeeded, links_failed);
320   GNUNET_assert (num == num_peers);
321   GNUNET_assert (0 == links_failed);
322   /* Collect peer information */
323   all_peers_info = GNUNET_new_array (num_peers, struct PeerInfo);
324
325   for (i = 0; i < num_peers; i++)
326   {
327     GNUNET_TESTBED_peer_get_information (peers[i],
328                                          GNUNET_TESTBED_PIT_CONFIGURATION,
329                                          &peer_info_cb, peers[i]);
330   }
331 }
332
333
334 /**
335  * Verify that the user passed correct CL args
336  *
337  * @return #GNUNET_OK if arguments are valid, #GNUNET_SYSERR otherwise
338  */
339 static int
340 verify_args ()
341 {
342   if (num_peers < 3)
343   {
344     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
345                 _
346                 ("Invalid or missing number of peers. Set at least 3 peers.\n"));
347     return GNUNET_SYSERR;
348   }
349   return GNUNET_OK;
350 }
351
352
353 /**
354  * Actual main function.
355  *
356  * @param cls unused
357  * @param args remaining args, unused
358  * @param cfgfile name of the configuration
359  * @param cfg configuration handle
360  */
361 static void
362 run (void *cls, char *const *args, const char *cf,
363      const struct GNUNET_CONFIGURATION_Handle *c)
364 {
365   struct GNUNET_CONFIGURATION_Handle *cfg;
366   double links;
367
368   if (GNUNET_OK != verify_args ())
369   {
370     do_shutdown ();
371     return;
372   }
373   cfg = GNUNET_CONFIGURATION_create ();
374   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (cfg, cfg_filename));
375   links = log (num_peers) * log (num_peers) * num_peers / 2;
376   GNUNET_CONFIGURATION_set_value_number ((struct GNUNET_CONFIGURATION_Handle *)
377                                          cfg, "TESTBED", "OVERLAY_RANDOM_LINKS",
378                                          (unsigned long long int) links);
379   GNUNET_TESTBED_run (NULL, cfg, num_peers, 0, NULL, NULL, &test_master, NULL);
380   GNUNET_CONFIGURATION_destroy (cfg);
381 }
382
383
384 /**
385  * Main function.
386  *
387  * @return 0 on success
388  */
389 int
390 main (int argc, char *const *argv)
391 {
392   static struct GNUNET_GETOPT_CommandLineOption options[] = {
393     {'p', "peers", "COUNT", gettext_noop ("Number of peers to run"), GNUNET_YES,
394      &GNUNET_GETOPT_set_uint, &num_peers},
395     {'i', "sensors-interval", "INTERVAL",
396      gettext_noop ("Change the interval or running sensors to given value"),
397      GNUNET_YES, &GNUNET_GETOPT_set_uint, &sensors_interval},
398     GNUNET_GETOPT_OPTION_END
399   };
400
401   return (GNUNET_OK ==
402           GNUNET_PROGRAM_run (argc, argv, "gnunet-sensor-profiler",
403                               gettext_noop ("Profiler for sensor service"),
404                               options, &run, NULL)) ? ok : 1;
405 }
406
407 /* end of gnunet-sensor-profiler.c */