glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / testbed / generate-underlay-topology.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2008--2014 GNUnet e.V.
4
5       GNUnet is free software: you can redistribute it and/or modify it
6       under the terms of the GNU Affero General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your 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       Affero General Public License for more details.
14 */
15
16 /**
17  * @file testbed/generate-underlay-topology.c
18  * @brief Program to generate a database file containing given underlay topology
19  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
20  */
21
22 #include "platform.h"
23 #include "gnunet_util_lib.h"
24 #include "gnunet_testbed_service.h"
25 #include "testbed_api_topology.h"
26 #include "sqlite3.h"
27
28 #define LOG(type, ...)                          \
29   GNUNET_log (type, __VA_ARGS__)
30
31
32 #define LOG_ERROR(...)                          \
33   LOG (GNUNET_ERROR_TYPE_ERROR, __VA_ARGS__)
34
35 /**
36  * Log an error message at log-level 'level' that indicates
37  * a failure of the command 'cmd' on file 'filename'
38  * with the message given by strerror(errno).
39  */
40 #define LOG_SQLITE(db, msg, level, cmd)                                 \
41   do {                                                                  \
42     GNUNET_log_from (level, "sqlite", _("`%s' failed at %s:%d with error: %s\n"), \
43                      cmd, __FILE__,__LINE__, sqlite3_errmsg(db));  \
44     if (msg != NULL)                                                    \
45       GNUNET_asprintf(msg, _("`%s' failed at %s:%u with error: %s"), cmd, \
46                       __FILE__, __LINE__, sqlite3_errmsg(db));     \
47   } while(0)
48
49
50 /**
51  * Handle to the sqlite3 database
52  */
53 static struct sqlite3 *db;
54
55 /**
56  * Prepared statement for inserting link values into db
57  */
58 struct sqlite3_stmt *stmt_insert;
59
60 /**
61  * The topology to generate
62  */
63 enum GNUNET_TESTBED_TopologyOption topology;
64
65 /**
66  * The number of peers to include in the topology
67  */
68 static unsigned int num_peers;
69
70 /**
71  * program result
72  */
73 static int exit_result;
74
75
76 /**
77  * Functions of this type are called to process underlay link
78  *
79  * @param cls closure
80  * @param A offset of first peer
81  * @param B offset of second peer
82  * @param bandwidth the bandwidth of the link in bytes per second
83  * @param latency the latency of link in milliseconds
84  * @param loss the percentage of messages dropped on the link
85  * @return GNUNET_OK to continue processing; GNUNET_SYSERR to abort
86  */
87 static int
88 link_processor (void *cls,
89                 unsigned int A,
90                 unsigned int B,
91                 unsigned int bandwidth,
92                 unsigned int latency,
93                 unsigned int loss)
94 {
95   if ( (SQLITE_OK != sqlite3_bind_int (stmt_insert, 1, A)) ||
96        (SQLITE_OK != sqlite3_bind_int (stmt_insert, 2, B)) ||
97        (SQLITE_OK != sqlite3_bind_int (stmt_insert, 3, bandwidth)) ||
98        (SQLITE_OK != sqlite3_bind_int (stmt_insert, 4, latency)) ||
99        (SQLITE_OK != sqlite3_bind_int (stmt_insert, 5, loss)) )
100   {
101     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_bind_int");
102     return GNUNET_SYSERR;
103   }
104   if (SQLITE_DONE != sqlite3_step (stmt_insert))
105   {
106     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_step");
107     return GNUNET_SYSERR;
108   }
109   FPRINTF (stdout, "%u -> %u\n", A, B);
110   GNUNET_break (SQLITE_OK == sqlite3_reset (stmt_insert));
111   //GNUNET_break (SQLITE_OK == sqlite3_clear_bindings (stmt_insert));
112   if ( (SQLITE_OK != sqlite3_bind_int (stmt_insert, 1, B)) ||
113        (SQLITE_OK != sqlite3_bind_int (stmt_insert, 2, A)) )
114   {
115     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_bind_int");
116     return GNUNET_SYSERR;
117   }
118   if (SQLITE_DONE != sqlite3_step (stmt_insert))
119   {
120     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_step");
121     return GNUNET_SYSERR;
122   }
123   FPRINTF (stdout, "%u -> %u\n", B, A);
124   GNUNET_break (SQLITE_OK == sqlite3_reset (stmt_insert));
125   return GNUNET_OK;
126 }
127
128
129 /**
130  * Open the database file, creating a new database if not existing and setup the
131  * whitelist table
132  *
133  * @param dbfile the database filename
134  * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure (error message has
135  * to be printed)
136  */
137 static int
138 setup_db (const char *dbfile)
139 {
140   const char *query_create =
141       "CREATE TABLE whitelist ("
142       "id INTEGER,"
143       "oid INTEGER,"
144       "bandwidth INTEGER DEFAULT NULL,"
145       "latency INTEGER DEFAULT NULL,"
146       "loss INTEGER DEFAULT NULL,"
147       " UNIQUE ("
148       "  id,"
149       "  oid"
150       " ) ON CONFLICT IGNORE"
151       ");";
152   const char *query_insert =
153       "INSERT INTO whitelist("
154       " id,"
155       " oid,"
156       " bandwidth,"
157       " latency,"
158       " loss"
159       ") VALUES ("
160       " ?1,"
161       " ?2,"
162       " ?3,"
163       " ?4,"
164       " ?5);";
165   int ret;
166
167   ret = GNUNET_SYSERR;
168   if (SQLITE_OK != sqlite3_open (dbfile, &db))
169   {
170     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_open");
171     goto err_ret;
172   }
173   if (0 != sqlite3_exec (db, query_create, NULL, NULL, NULL))
174   {
175     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_exec");
176     FPRINTF (stderr, "Error: %d.  Perhaps the database `%s' already exits.\n",
177              sqlite3_errcode (db),
178              dbfile);
179     goto err_ret;
180   }
181   GNUNET_break (0 == sqlite3_exec (db, "PRAGMA synchronous = 0;", NULL, NULL, NULL));
182   if (SQLITE_OK != sqlite3_prepare_v2 (db, query_insert, -1,
183                                        &stmt_insert, NULL))
184   {
185     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_prepare_v2");
186     goto err_ret;
187   }
188   ret = GNUNET_OK;
189
190  err_ret:
191   return ret;
192 }
193
194
195 /**
196  * Main run function.
197  *
198  * @param cls NULL
199  * @param args arguments passed to GNUNET_PROGRAM_run
200  * @param cfgfile the path to configuration file
201  * @param cfg the configuration file handle
202  */
203 static void
204 run (void *cls, char *const *args, const char *cfgfile,
205      const struct GNUNET_CONFIGURATION_Handle *config)
206 {
207   const char *dbfile;
208   const char *topology_string;
209   unsigned int arg_uint1;
210   unsigned int arg_uint2;
211   const char *arg_str1;
212   const char *value;
213   unsigned int argc;
214
215   argc = 0;
216   arg_uint1 = 0; /* make compilers happy */
217   arg_uint2 = 0; /* make compilers happy */
218   if (NULL == args)
219   {
220     LOG_ERROR (_("Need at least 2 arguments\n"));
221     return;
222   }
223   if (NULL == (dbfile = args[argc++]))
224   {
225     LOG_ERROR (_("Database filename missing\n"));
226     return;
227   }
228   if (GNUNET_OK != setup_db (dbfile))
229     return;
230   if (NULL == (topology_string = args[argc++]))
231   {
232     LOG_ERROR (_("Topology string missing\n"));
233     return;
234   }
235   if (GNUNET_YES != GNUNET_TESTBED_topology_get_ (&topology, topology_string))
236   {
237     LOG_ERROR (_("Invalid topology: %s\n"), topology_string);
238     return;
239   }
240   arg_str1 = NULL;
241   /* parse for first TOPOOPT.  This can either be arg_uint1 or arg_str1 */
242   switch (topology)
243   {
244   case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
245   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING:
246   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD:
247   case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
248     if (NULL == (value = args[argc++]))
249     {
250       LOG_ERROR (_("An argument is missing for given topology `%s'\n"),
251                  topology_string);
252       return;
253     }
254     if (-1 == SSCANF (value, "%u", &arg_uint1))
255     {
256       LOG_ERROR (_("Invalid argument `%s' given as topology argument\n"),
257                  value);
258       return;
259     }
260     break;
261   case GNUNET_TESTBED_TOPOLOGY_FROM_FILE:
262     if (NULL == (arg_str1 = args[argc++]))
263     {
264       LOG_ERROR (_("Filename argument missing for topology `%s'\n"),
265                  topology_string);
266       return;
267     }
268     break;
269   default:
270     break;
271   }
272   /* parse for second TOPOOPT.  Only required for SCALE_FREE topology */
273   switch (topology)
274   {
275   case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
276     if (NULL == (value = args[argc++]))
277     {
278       LOG_ERROR (_("Second argument for topology `%s' is missing\n"),
279                  topology_string);
280       return;
281     }
282     if (-1 == SSCANF (value, "%u", &arg_uint2))
283     {
284       LOG_ERROR (_("Invalid argument `%s'; expecting unsigned int\n"), value);
285       return;
286     }
287     break;
288   default:
289     break;
290   }
291   /* contruct topologies */
292   switch (topology)
293   {
294   case GNUNET_TESTBED_TOPOLOGY_LINE:
295   case GNUNET_TESTBED_TOPOLOGY_RING:
296   case GNUNET_TESTBED_TOPOLOGY_STAR:
297   case GNUNET_TESTBED_TOPOLOGY_CLIQUE:
298   case GNUNET_TESTBED_TOPOLOGY_2D_TORUS:
299     GNUNET_TESTBED_underlay_construct_ (num_peers, link_processor, NULL,
300                                         topology);
301     break;
302   case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
303   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING:
304   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD:
305     GNUNET_TESTBED_underlay_construct_ (num_peers, link_processor, NULL,
306                                         topology,
307                                         arg_uint1);
308     break;
309   case GNUNET_TESTBED_TOPOLOGY_FROM_FILE:
310     GNUNET_TESTBED_underlay_construct_ (num_peers, link_processor, NULL,
311                                         topology,
312                                         arg_str1);
313     break;
314   case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
315     GNUNET_TESTBED_underlay_construct_ (num_peers, link_processor, NULL,
316                                         topology,
317                                         arg_uint1,
318                                         arg_uint2);
319     break;
320   default:
321     GNUNET_assert (0);
322   }
323 }
324
325
326 /**
327  * Main
328  */
329 int
330 main (int argc, char *const argv[])
331 {
332   struct GNUNET_GETOPT_CommandLineOption option[] = {
333
334     GNUNET_GETOPT_option_uint ('p',
335                                    "num-peers",
336                                    "COUNT",
337                                    gettext_noop ("create COUNT number of peers"),
338                                    &num_peers),
339     GNUNET_GETOPT_OPTION_END
340   };
341
342   int ret;
343
344   exit_result = GNUNET_SYSERR;
345   ret =
346       GNUNET_PROGRAM_run (argc, argv, "gnunet-underlay-topology",
347                           _("Generates SQLite3 database representing a given underlay topology.\n"
348                             "Usage: gnunet-underlay-topology [OPTIONS] db-filename TOPO [TOPOOPTS]\n"
349                             "The following options are available for TOPO followed by TOPOOPTS if applicable:\n"
350                             "\t LINE\n"
351                             "\t RING\n"
352                             "\t RANDOM <num_rnd_links>\n"
353                             "\t SMALL_WORLD <num_rnd_links>\n"
354                             "\t SMALL_WORLD_RING <num_rnd_links>\n"
355                             "\t CLIQUE\n"
356                             "\t 2D_TORUS\n"
357                             "\t SCALE_FREE <cap> <m>\n"
358                             "\t FROM_FILE <filename>\n"
359                             "TOPOOPTS:\n"
360                             "\t num_rnd_links: The number of random links\n"
361                             "\t cap: the maximum number of links a node can have\n"
362                             "\t m: the number of links a node should have while joining the network\n"
363                             "\t filename: the path of the file which contains topology information\n"
364                             "NOTE: the format of the above file is descibed here: https://www.gnunet.org/content/topology-file-format\n"),
365                           option, &run, NULL);
366   if (NULL != stmt_insert)
367     sqlite3_finalize (stmt_insert);
368   if (NULL != db)
369     GNUNET_break (SQLITE_OK == sqlite3_close (db));
370   if ((GNUNET_OK != ret) || (GNUNET_OK != exit_result))
371     return 1;
372   return 0;
373 }