fix gnunet-identity performance
[oweals/gnunet.git] / src / identity / gnunet-identity.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013, 2018, 2019 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  * @file identity/gnunet-identity.c
22  * @brief IDENTITY management command line tool
23  * @author Christian Grothoff
24  *
25  * Todo:
26  * - add options to get default egos
27  */
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_identity_service.h"
31
32
33 /**
34  * Return value from main on timeout.
35  */
36 #define TIMEOUT_STATUS_CODE 40
37
38 /**
39  * Handle to IDENTITY service.
40  */
41 static struct GNUNET_IDENTITY_Handle *sh;
42
43 /**
44  * Was "list" specified?
45  */
46 static int list;
47
48 /**
49  * Was "monitor" specified?
50  */
51 static int monitor;
52
53 /**
54  * Was "private" specified?
55  */
56 static int private_keys;
57
58 /**
59  * Was "verbose" specified?
60  */
61 static unsigned int verbose;
62
63 /**
64  * Was "quiet" specified?
65  */
66 static int quiet;
67
68 /**
69  * -C option
70  */
71 static char *create_ego;
72
73 /**
74  * -D option
75  */
76 static char *delete_ego;
77
78 /**
79  * -s option.
80  */
81 static char *set_ego;
82
83 /**
84  * -S option.
85  */
86 static char *set_subsystem;
87
88 /**
89  * Operation handle for set operation.
90  */
91 static struct GNUNET_IDENTITY_Operation *set_op;
92
93 /**
94  * Handle for create operation.
95  */
96 static struct GNUNET_IDENTITY_Operation *create_op;
97
98 /**
99  * Handle for delete operation.
100  */
101 static struct GNUNET_IDENTITY_Operation *delete_op;
102
103 /**
104  * Value to return from #main().
105  */
106 static int global_ret;
107
108
109 /**
110  * Task run on shutdown.
111  *
112  * @param cls NULL
113  */
114 static void
115 shutdown_task (void *cls)
116 {
117   if (NULL != set_op)
118   {
119     GNUNET_IDENTITY_cancel (set_op);
120     set_op = NULL;
121   }
122   if (NULL != create_op)
123   {
124     GNUNET_IDENTITY_cancel (create_op);
125     create_op = NULL;
126   }
127   if (NULL != delete_op)
128   {
129     GNUNET_IDENTITY_cancel (delete_op);
130     delete_op = NULL;
131   }
132   if (NULL != set_ego)
133   {
134     GNUNET_free (set_ego);
135     set_ego = NULL;
136   }
137   GNUNET_IDENTITY_disconnect (sh);
138   sh = NULL;
139 }
140
141
142 /**
143  * Test if we are finished yet.
144  */
145 static void
146 test_finished (void)
147 {
148   if ( (NULL == create_op) &&
149        (NULL == delete_op) &&
150        (NULL == set_op) &&
151        (NULL == set_subsystem) &&
152        (! list) &&
153        (! monitor))
154   {
155     if (TIMEOUT_STATUS_CODE == global_ret)
156       global_ret = 0;
157     GNUNET_SCHEDULER_shutdown ();
158   }
159 }
160
161
162 /**
163  * Deletion operation finished.
164  *
165  * @param cls pointer to operation handle
166  * @param emsg NULL on success, otherwise an error message
167  */
168 static void
169 delete_finished (void *cls,
170                  const char *emsg)
171 {
172   struct GNUNET_IDENTITY_Operation **op = cls;
173
174   *op = NULL;
175   if (NULL != emsg)
176     fprintf (stderr, "%s\n", gettext (emsg));
177   test_finished ();
178 }
179
180
181 /**
182  * Creation operation finished.
183  *
184  * @param cls pointer to operation handle
185  * @param pk private key of the ego, or NULL on error
186  * @param emsg error message, NULL on success
187  */
188 static void
189 create_finished (void *cls,
190                  const struct GNUNET_CRYPTO_EcdsaPrivateKey *pk,
191                  const char *emsg)
192 {
193   struct GNUNET_IDENTITY_Operation **op = cls;
194
195   *op = NULL;
196   if (NULL == pk)
197   {
198     fprintf (stderr,
199              _ ("Failed to create ego: %s\n"),
200              emsg);
201     global_ret = 1;
202   }
203   else if (verbose)
204   {
205     struct GNUNET_CRYPTO_EcdsaPublicKey pub;
206     char *pubs;
207
208     GNUNET_CRYPTO_ecdsa_key_get_public (pk, &pub);
209     pubs = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pub);
210     if (private_keys)
211     {
212       char *privs;
213
214       privs = GNUNET_CRYPTO_ecdsa_private_key_to_string (pk);
215       fprintf (stdout, "%s - %s\n", pubs, privs);
216       GNUNET_free (privs);
217     }
218     else
219     {
220       fprintf (stdout, "%s\n", pubs);
221     }
222     GNUNET_free (pubs);
223   }
224   test_finished ();
225 }
226
227
228 /**
229  * Function called by #GNUNET_IDENTITY_set up on completion.
230  *
231  * @param cls NULL
232  * @param emsg error message (NULL on success)
233  */
234 static void
235 set_done (void *cls, const char *emsg)
236 {
237   set_op = NULL;
238   if (NULL != emsg)
239   {
240     fprintf (stderr, _ ("Failed to set default ego: %s\n"), emsg);
241     global_ret = 1;
242   }
243   test_finished ();
244 }
245
246
247 /**
248  * If listing is enabled, prints information about the egos.
249  *
250  * This function is initially called for all egos and then again
251  * whenever a ego's identifier changes or if it is deleted.  At the
252  * end of the initial pass over all egos, the function is once called
253  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
254  * be invoked in the future or that there was an error.
255  *
256  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
257  * this function is only called ONCE, and 'NULL' being passed in
258  * 'ego' does indicate an error (i.e. name is taken or no default
259  * value is known).  If 'ego' is non-NULL and if '*ctx'
260  * is set in those callbacks, the value WILL be passed to a subsequent
261  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
262  * that one was not NULL).
263  *
264  * When an identity is renamed, this function is called with the
265  * (known) ego but the NEW identifier.
266  *
267  * When an identity is deleted, this function is called with the
268  * (known) ego and "NULL" for the 'identifier'.  In this case,
269  * the 'ego' is henceforth invalid (and the 'ctx' should also be
270  * cleaned up).
271  *
272  * @param cls closure
273  * @param ego ego handle
274  * @param ctx context for application to store data for this ego
275  *                 (during the lifetime of this process, initially NULL)
276  * @param identifier identifier assigned by the user for this ego,
277  *                   NULL if the user just deleted the ego and it
278  *                   must thus no longer be used
279  */
280 static void
281 print_ego (void *cls,
282            struct GNUNET_IDENTITY_Ego *ego,
283            void **ctx,
284            const char *identifier)
285 {
286   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
287   char *s;
288   char *privs;
289
290   if ( (NULL != set_ego) &&
291        (NULL != set_subsystem) &&
292        (NULL != ego) &&
293        (NULL != identifier) &&
294        (0 == strcmp (identifier, set_ego)))
295   {
296     set_op = GNUNET_IDENTITY_set (sh,
297                                   set_subsystem,
298                                   ego,
299                                   &set_done,
300                                   NULL);
301     GNUNET_free (set_subsystem);
302     set_subsystem = NULL;
303     GNUNET_free (set_ego);
304     set_ego = NULL;
305   }
306   if ( (NULL == ego) &&
307        (NULL != set_ego) &&
308        (NULL != set_subsystem) )
309   {
310     fprintf (stderr,
311              "Could not set ego to `%s' for subsystem `%s', ego not known\n",
312              set_ego,
313              set_subsystem);
314     GNUNET_free (set_subsystem);
315     set_subsystem = NULL;
316     GNUNET_free (set_ego);
317     set_ego = NULL;
318   }
319   if ((NULL == ego) && (! monitor))
320   {
321     list = 0;
322     test_finished ();
323     return;
324   }
325   if (! (list | monitor))
326     return;
327   if ( (NULL == ego) ||
328        (NULL == identifier) )
329     return;
330   if ( (NULL != set_ego) &&
331        (0 != strcmp (identifier,
332                      set_ego)) )
333     return;
334   GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
335   s = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
336   privs = GNUNET_CRYPTO_ecdsa_private_key_to_string (
337     GNUNET_IDENTITY_ego_get_private_key (ego));
338   if ((monitor) || (NULL != identifier))
339   {
340     if (quiet)
341     {
342       if (private_keys)
343         fprintf (stdout, "%s - %s\n", s, privs);
344       else
345         fprintf (stdout, "%s\n", s);
346     }
347     else
348     {
349       if (private_keys)
350         fprintf (stdout, "%s - %s - %s\n", identifier, s, privs);
351       else
352         fprintf (stdout, "%s - %s\n", identifier, s);
353     }
354   }
355   GNUNET_free (privs);
356   GNUNET_free (s);
357 }
358
359
360 /**
361  * Main function that will be run by the scheduler.
362  *
363  * @param cls closure
364  * @param args remaining command-line arguments
365  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
366  * @param cfg configuration
367  */
368 static void
369 run (void *cls,
370      char *const *args,
371      const char *cfgfile,
372      const struct GNUNET_CONFIGURATION_Handle *cfg)
373 {
374   if ((NULL != set_subsystem) && (NULL == set_ego))
375   {
376     fprintf (stderr, "Option -s requires option -e to be specified as well.\n");
377     return;
378   }
379   sh = GNUNET_IDENTITY_connect (cfg,
380                                 (monitor | list) ||
381                                 (NULL != set_ego) ||
382                                 (NULL != set_subsystem)
383                                 ? &print_ego
384                                 : NULL,
385                                 NULL);
386   if (NULL != delete_ego)
387     delete_op =
388       GNUNET_IDENTITY_delete (sh,
389                               delete_ego,
390                               &delete_finished,
391                               &delete_op);
392   if (NULL != create_ego)
393     create_op =
394       GNUNET_IDENTITY_create (sh,
395                               create_ego,
396                               &create_finished,
397                               &create_op);
398   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
399                                  NULL);
400   test_finished ();
401 }
402
403
404 /**
405  * The main function.
406  *
407  * @param argc number of arguments from the command line
408  * @param argv command line arguments
409  * @return 0 ok, 1 on error
410  */
411 int
412 main (int argc, char *const *argv)
413 {
414   struct GNUNET_GETOPT_CommandLineOption options[] = {
415     GNUNET_GETOPT_option_string ('C',
416                                  "create",
417                                  "NAME",
418                                  gettext_noop ("create ego NAME"),
419                                  &create_ego),
420     GNUNET_GETOPT_option_string ('D',
421                                  "delete",
422                                  "NAME",
423                                  gettext_noop ("delete ego NAME "),
424                                  &delete_ego),
425     GNUNET_GETOPT_option_flag ('d',
426                                "display",
427                                gettext_noop ("display all egos"),
428                                &list),
429     GNUNET_GETOPT_option_flag ('q',
430                                "quiet",
431                                gettext_noop ("reduce output"),
432                                &quiet),
433     GNUNET_GETOPT_option_string (
434       'e',
435       "ego",
436       "NAME",
437       gettext_noop (
438         "set default identity to NAME for a subsystem SUBSYSTEM (use together with -s) or restrict results to NAME (use together with -d)"),
439       &set_ego),
440     GNUNET_GETOPT_option_flag ('m',
441                                "monitor",
442                                gettext_noop ("run in monitor mode egos"),
443                                &monitor),
444     GNUNET_GETOPT_option_flag ('p',
445                                "private-keys",
446                                gettext_noop ("display private keys as well"),
447                                &private_keys),
448     GNUNET_GETOPT_option_string (
449       's',
450       "set",
451       "SUBSYSTEM",
452       gettext_noop (
453         "set default identity to EGO for a subsystem SUBSYSTEM (use together with -e)"),
454       &set_subsystem),
455     GNUNET_GETOPT_option_verbose (&verbose),
456     GNUNET_GETOPT_OPTION_END
457   };
458   int res;
459
460   if (GNUNET_OK !=
461       GNUNET_STRINGS_get_utf8_args (argc, argv,
462                                     &argc, &argv))
463     return 4;
464   global_ret = TIMEOUT_STATUS_CODE; /* timeout */
465   res = GNUNET_PROGRAM_run (argc,
466                             argv,
467                             "gnunet-identity",
468                             gettext_noop ("Maintain egos"),
469                             options,
470                             &run,
471                             NULL);
472   GNUNET_free_nz ((void *) argv);
473
474   if (GNUNET_OK != res)
475     return 3;
476   return global_ret;
477 }
478
479
480 /* end of gnunet-identity.c */