add changelog
[oweals/gnunet.git] / src / testing / gnunet-testing.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 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      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file testing/gnunet-testing.c
23  * @brief tool to use testing functionality from cmd line
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_testing_lib.h"
29
30
31 #define LOG(kind, ...) GNUNET_log_from (kind, "gnunet-testing", __VA_ARGS__)
32
33
34 /**
35  * Final status code.
36  */
37 static int ret;
38
39 /**
40  * Filename of the hostkey file we should write,
41  * null if we should not write a hostkey file.
42  */
43 static char *create_hostkey;
44
45 /**
46  * Non-zero if we should create config files.
47  */
48 static int create_cfg;
49
50 /**
51  * Number of config files to create.
52  */
53 static unsigned int create_no;
54
55 /**
56  * Filename of the config template to be written.
57  */
58 static char *create_cfg_template;
59
60 /**
61  * Service we are supposed to run.
62  */
63 static char *run_service_name;
64
65 /**
66  * File handle to STDIN, for reading restart/quit commands.
67  */
68 static struct GNUNET_DISK_FileHandle *fh;
69
70 /**
71  * Temporary filename, used with '-r' to write the configuration to.
72  */
73 static char *tmpfilename;
74
75 /**
76  * Task identifier of the task that waits for stdin.
77  */
78 static struct GNUNET_SCHEDULER_Task *tid;
79
80 /**
81  * Peer started for '-r'.
82  */
83 static struct GNUNET_TESTING_Peer *my_peer;
84
85
86 static int
87 create_unique_cfgs (const char *template, const unsigned int no)
88 {
89   struct GNUNET_TESTING_System *system;
90   int fail;
91   unsigned int cur;
92   char *cur_file;
93   struct GNUNET_CONFIGURATION_Handle *cfg_new;
94   struct GNUNET_CONFIGURATION_Handle *cfg_tmpl;
95
96   if (GNUNET_NO == GNUNET_DISK_file_test (template))
97   {
98     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
99                 "Configuration template `%s': file not found\n",
100                 create_cfg_template);
101     return 1;
102   }
103   cfg_tmpl = GNUNET_CONFIGURATION_create ();
104
105   /* load template */
106   if ((create_cfg_template != NULL) &&
107       (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg_tmpl, create_cfg_template)))
108   {
109     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
110                 "Could not load template `%s'\n",
111                 create_cfg_template);
112     GNUNET_CONFIGURATION_destroy (cfg_tmpl);
113
114     return 1;
115   }
116   /* load defaults */
117   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg_tmpl, NULL))
118   {
119     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
120                 "Could not load template `%s'\n",
121                 create_cfg_template);
122     GNUNET_CONFIGURATION_destroy (cfg_tmpl);
123     return 1;
124   }
125
126   fail = GNUNET_NO;
127   system =
128     GNUNET_TESTING_system_create ("testing", NULL /* controller */, NULL, NULL);
129   for (cur = 0; cur < no; cur++)
130   {
131     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
132                 "Creating configuration no. %u \n",
133                 cur);
134     if (create_cfg_template != NULL)
135       GNUNET_asprintf (&cur_file, "%04u-%s", cur, create_cfg_template);
136     else
137       GNUNET_asprintf (&cur_file, "%04u%s", cur, ".conf");
138
139     cfg_new = GNUNET_CONFIGURATION_dup (cfg_tmpl);
140     if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg_new))
141     {
142       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
143                   "Could not create another configuration\n");
144       GNUNET_CONFIGURATION_destroy (cfg_new);
145       fail = GNUNET_YES;
146       break;
147     }
148     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149                 "Writing configuration no. %u to file `%s' \n",
150                 cur,
151                 cur_file);
152     if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg_new, cur_file))
153     {
154       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
155                   "Failed to write configuration no. %u \n",
156                   cur);
157       fail = GNUNET_YES;
158     }
159     GNUNET_CONFIGURATION_destroy (cfg_new);
160     GNUNET_free (cur_file);
161     if (GNUNET_YES == fail)
162       break;
163   }
164   GNUNET_CONFIGURATION_destroy (cfg_tmpl);
165   GNUNET_TESTING_system_destroy (system, GNUNET_NO);
166   if (GNUNET_YES == fail)
167     return 1;
168   return 0;
169 }
170
171
172 static int
173 create_hostkeys (const unsigned int no)
174 {
175   struct GNUNET_TESTING_System *system;
176   struct GNUNET_PeerIdentity id;
177   struct GNUNET_DISK_FileHandle *fd;
178   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
179
180   system = GNUNET_TESTING_system_create ("testing", NULL, NULL, NULL);
181   pk = GNUNET_TESTING_hostkey_get (system, create_no, &id);
182   if (NULL == pk)
183   {
184     fprintf (stderr,
185              _ ("Could not extract hostkey %u (offset too large?)\n"),
186              create_no);
187     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
188     return 1;
189   }
190   (void) GNUNET_DISK_directory_create_for_file (create_hostkey);
191   fd =
192     GNUNET_DISK_file_open (create_hostkey,
193                            GNUNET_DISK_OPEN_READWRITE | GNUNET_DISK_OPEN_CREATE,
194                            GNUNET_DISK_PERM_USER_READ
195                            | GNUNET_DISK_PERM_USER_WRITE);
196   GNUNET_assert (fd != NULL);
197   ret = GNUNET_DISK_file_write (fd,
198                                 pk,
199                                 sizeof(struct GNUNET_CRYPTO_EddsaPrivateKey));
200   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd));
201   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
202                    "transport-testing",
203                    "Wrote hostkey to file: `%s'\n",
204                    create_hostkey);
205   GNUNET_free (pk);
206   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
207   return 0;
208 }
209
210
211 /**
212  * Cleanup called by signal handlers and when stdin is closed.
213  * Removes the temporary file.
214  *
215  * @param cls unused
216  */
217 static void
218 cleanup (void *cls)
219 {
220   if (NULL != tmpfilename)
221   {
222     if (0 != unlink (tmpfilename))
223       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
224                                 "unlink",
225                                 tmpfilename);
226   }
227   if (NULL != tid)
228   {
229     GNUNET_SCHEDULER_cancel (tid);
230     tid = NULL;
231   }
232   if (NULL != fh)
233   {
234     GNUNET_DISK_file_close (fh);
235     fh = NULL;
236   }
237 }
238
239
240 /**
241  * Called whenever we can read stdin non-blocking
242  *
243  * @param cls unused
244  */
245 static void
246 stdin_cb (void *cls)
247 {
248   int c;
249
250   tid = NULL;
251   c = getchar ();
252   switch (c)
253   {
254   case EOF:
255   case 'q':
256     GNUNET_SCHEDULER_shutdown ();
257     return;
258
259   case 'r':
260     if (GNUNET_OK != GNUNET_TESTING_peer_stop (my_peer))
261       LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to stop the peer\n");
262     if (GNUNET_OK != GNUNET_TESTING_peer_start (my_peer))
263       LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to start the peer\n");
264     printf ("restarted\n");
265     fflush (stdout);
266     break;
267
268   case '\n':
269   case '\r':
270     /* ignore whitespace */
271     break;
272
273   default:
274     fprintf (stderr,
275              _ ("Unknown command, use 'q' to quit or 'r' to restart peer\n"));
276     break;
277   }
278   tid = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
279                                         fh,
280                                         &stdin_cb,
281                                         NULL);
282 }
283
284
285 /**
286  * Main function called by the testing library.
287  * Executed inside a running scheduler.
288  *
289  * @param cls unused
290  * @param cfg configuration of the peer that was started
291  * @param peer handle to the peer
292  */
293 static void
294 testing_main (void *cls,
295               const struct GNUNET_CONFIGURATION_Handle *cfg,
296               struct GNUNET_TESTING_Peer *peer)
297 {
298   my_peer = peer;
299   if (NULL == (tmpfilename = GNUNET_DISK_mktemp ("gnunet-testing")))
300   {
301     GNUNET_break (0);
302     GNUNET_SCHEDULER_shutdown ();
303     return;
304   }
305   if (GNUNET_SYSERR ==
306       GNUNET_CONFIGURATION_write ((struct GNUNET_CONFIGURATION_Handle *) cfg,
307                                   tmpfilename))
308   {
309     GNUNET_break (0);
310     return;
311   }
312   printf ("ok\n%s\n", tmpfilename);
313   fflush (stdout);
314   GNUNET_SCHEDULER_add_shutdown (&cleanup, NULL);
315   fh = GNUNET_DISK_get_handle_from_native (stdin);
316   tid = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
317                                         fh,
318                                         &stdin_cb,
319                                         NULL);
320 }
321
322
323 /**
324  * Main function that will be running without scheduler.
325  *
326  * @param cls closure
327  * @param args remaining command-line arguments
328  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
329  * @param cfg configuration
330  */
331 static void
332 run_no_scheduler (void *cls,
333                   char *const *args,
334                   const char *cfgfile,
335                   const struct GNUNET_CONFIGURATION_Handle *cfg)
336 {
337   if (NULL != run_service_name)
338   {
339     ret = GNUNET_TESTING_service_run ("gnunet_service_test",
340                                       run_service_name,
341                                       cfgfile,
342                                       &testing_main,
343                                       NULL);
344     return;
345   }
346
347   if (GNUNET_YES == create_cfg)
348   {
349     if (create_no > 0)
350     {
351       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352                   "Creating %u configuration files based on template `%s'\n",
353                   create_no,
354                   create_cfg_template);
355       ret = create_unique_cfgs (create_cfg_template, create_no);
356     }
357     else
358     {
359       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing arguments! \n");
360       ret = 1;
361     }
362   }
363   if (NULL != create_hostkey)
364   {
365     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Extracting hostkey %u\n", create_no);
366     ret = create_hostkeys (create_no);
367   }
368   GNUNET_free_non_null (create_cfg_template);
369 }
370
371
372 /**
373  * The main function.
374  *
375  * @param argc number of arguments from the command line
376  * @param argv command line arguments
377  * @return 0 ok, 1 on error
378  */
379 int
380 main (int argc, char *const *argv)
381 {
382   struct GNUNET_GETOPT_CommandLineOption options[] =
383   { GNUNET_GETOPT_option_flag ('C',
384                                "cfg",
385                                gettext_noop (
386                                  "create unique configuration files"),
387                                &create_cfg),
388     GNUNET_GETOPT_option_string (
389       'k',
390       "key",
391       "FILENAME",
392       gettext_noop ("extract hostkey file from pre-computed hostkey list"),
393       &create_hostkey),
394
395     GNUNET_GETOPT_option_uint (
396       'n',
397       "number",
398       "NUMBER",
399       gettext_noop (
400         "number of unique configuration files to create, or number of the hostkey to extract"),
401       &create_no),
402
403
404     GNUNET_GETOPT_option_string ('t',
405                                  "template",
406                                  "FILENAME",
407                                  gettext_noop ("configuration template"),
408                                  &create_cfg_template),
409
410     GNUNET_GETOPT_option_string (
411       'r',
412       "run",
413       "SERVICE",
414       gettext_noop (
415         "run the given service, wait on stdin for 'r' (restart) or 'q' (quit)"),
416       &run_service_name),
417     GNUNET_GETOPT_OPTION_END };
418
419   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
420     return 2;
421
422   /* Run without scheduler, because we may want to call
423    * GNUNET_TESTING_service_run, which starts the scheduler on its own.
424    * Furthermore, the other functionality currently does not require the scheduler, too,
425    * but beware when extending gnunet-testing. */
426   ret =
427     (GNUNET_OK ==
428      GNUNET_PROGRAM_run2 (argc,
429                           argv,
430                           "gnunet-testing",
431                           gettext_noop (
432                             "Command line tool to access the testing library"),
433                           options,
434                           &run_no_scheduler,
435                           NULL,
436                           GNUNET_YES))
437     ? ret
438     : 1;
439   GNUNET_free ((void *) argv);
440   return ret;
441 }
442
443
444 /* end of gnunet-testing.c */