anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

anastasis-cli-redux.c (10640B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020,2021,2022 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file cli/anastasis-cli-redux.c
     18  * @brief command line tool for our reducer
     19  * @author Christian Grothoff
     20  * @author Dennis Neufeld
     21  * @author Dominik Meister
     22  */
     23 
     24 #include "platform.h"
     25 #include <gnunet/gnunet_util_lib.h>
     26 #include <gnunet/gnunet_curl_lib.h>
     27 #include "anastasis_redux.h"
     28 #include <taler/taler_util.h>
     29 #include <taler/taler_error_codes.h>
     30 #include <taler/taler_json_lib.h>
     31 #include "anastasis_util_lib.h"
     32 #include "anastasis-cli-common.h"
     33 
     34 /**
     35  * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
     36  */
     37 static struct GNUNET_CURL_RescheduleContext *rc;
     38 
     39 /**
     40  * Curl context for communication with anastasis backend
     41  */
     42 static struct GNUNET_CURL_Context *ctx;
     43 
     44 /**
     45  * Application ID to include in the user attributes.
     46  * (-a option).
     47  */
     48 char *application_id;
     49 
     50 /**
     51  * -b option given.
     52  */
     53 static int b_flag;
     54 
     55 /**
     56  * -r option given.
     57  */
     58 static int r_flag;
     59 
     60 /**
     61  * Input to -a option given.
     62  */
     63 static char *input;
     64 
     65 /**
     66  * Output filename, if given.
     67  */
     68 static char *output_filename;
     69 
     70 /**
     71  * JSON containing previous state
     72  */
     73 static json_t *prev_state;
     74 
     75 /**
     76  * JSON containing arguments for action
     77  */
     78 static json_t *arguments;
     79 
     80 /**
     81  * Handle to an ongoing action.
     82  */
     83 static struct ANASTASIS_ReduxAction *ra;
     84 
     85 /**
     86  * Return value from main.
     87  */
     88 static int global_ret;
     89 
     90 
     91 /**
     92  * Persist a json state, report errors.
     93  *
     94  * @param state to persist
     95  * @param filename where to write the state to, NULL for stdout
     96  */
     97 static void
     98 persist_new_state (json_t *state,
     99                    const char *filename)
    100 {
    101   if (NULL != filename)
    102   {
    103     if (0 !=
    104         json_dump_file (state,
    105                         filename,
    106                         JSON_COMPACT))
    107     {
    108       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    109                   "Could not dump state to `%s'\n",
    110                   filename);
    111       global_ret = 1;
    112       return;
    113     }
    114     return;
    115   }
    116   {
    117     char *state_str = json_dumps (state,
    118                                   JSON_COMPACT);
    119 
    120     if (NULL == state_str)
    121     {
    122       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    123                   "Could not serialize state to JSON\n");
    124       global_ret = 1;
    125       return;
    126     }
    127     if (-1 >=
    128         fprintf (stdout,
    129                  "%s",
    130                  state_str))
    131     {
    132       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    133                   "Could not dump state to stdout\n");
    134       global_ret = 1;
    135       GNUNET_free (state_str);
    136       return;
    137     }
    138     GNUNET_free (state_str);
    139   }
    140 }
    141 
    142 
    143 /**
    144  * Function called with the results of #ANASTASIS_redux_action().
    145  *
    146  * @param cls closure
    147  * @param error_code Error code
    148  * @param result_state new state as result
    149  */
    150 static void
    151 action_cb (void *cls,
    152            enum TALER_ErrorCode error_code,
    153            json_t *result_state)
    154 {
    155   (void) cls;
    156   ra = NULL;
    157   if (NULL != result_state)
    158     persist_new_state (result_state,
    159                        output_filename);
    160   if (TALER_EC_NONE != error_code)
    161   {
    162     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    163                 "Redux failed with error %d: %s\n",
    164                 error_code,
    165                 TALER_ErrorCode_get_hint (error_code));
    166     json_dumpf (result_state,
    167                 stderr,
    168                 JSON_INDENT (2));
    169   }
    170   GNUNET_SCHEDULER_shutdown ();
    171   global_ret = (TALER_EC_NONE != error_code) ? 1 : 0;
    172 }
    173 
    174 
    175 /**
    176  * @brief Shutdown the application.
    177  *
    178  * @param cls closure
    179  */
    180 static void
    181 shutdown_task (void *cls)
    182 {
    183   (void) cls;
    184 
    185   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    186               "Shutdown initiated\n");
    187   if (NULL != ra)
    188   {
    189     ANASTASIS_redux_action_cancel (ra);
    190     ra = NULL;
    191   }
    192   ANASTASIS_redux_done ();
    193   if (NULL != ctx)
    194   {
    195     GNUNET_CURL_fini (ctx);
    196     ctx = NULL;
    197   }
    198   if (NULL != rc)
    199   {
    200     GNUNET_CURL_gnunet_rc_destroy (rc);
    201     rc = NULL;
    202   }
    203   json_decref (prev_state);
    204   prev_state = NULL;
    205   json_decref (arguments);
    206   arguments = NULL;
    207   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    208               "Shutdown complete\n");
    209 }
    210 
    211 
    212 /**
    213  * @brief Start the application
    214  *
    215  * @param cls closure
    216  * @param args arguments left
    217  * @param cfgfile config file name
    218  * @param cfg handle for the configuration file
    219  */
    220 static void
    221 run (void *cls,
    222      char *const *args,
    223      const char *cfgfile,
    224      const struct GNUNET_CONFIGURATION_Handle *cfg)
    225 {
    226   json_error_t error;
    227 
    228   (void) cls;
    229   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    230               "Starting anastasis-reducer\n");
    231   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
    232                                  NULL);
    233   if (b_flag && r_flag)
    234   {
    235     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    236                 "We cannot start backup and recovery at the same time!\n");
    237     global_ret = 1;
    238     GNUNET_SCHEDULER_shutdown ();
    239     return;
    240   }
    241   if (r_flag)
    242   {
    243     json_t *init_state;
    244 
    245     init_state = ANASTASIS_recovery_start (cfg);
    246     if (NULL == init_state)
    247     {
    248       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    249                   "Failed to create an initial recovery state!\n");
    250       global_ret = 1;
    251       GNUNET_SCHEDULER_shutdown ();
    252       return;
    253     }
    254     persist_new_state (init_state,
    255                        args[0]);
    256     json_decref (init_state);
    257     GNUNET_SCHEDULER_shutdown ();
    258     return;
    259   }
    260   if (b_flag)
    261   {
    262     json_t *init_state;
    263 
    264     init_state = ANASTASIS_backup_start (cfg);
    265     if (NULL == init_state)
    266     {
    267       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    268                   "Failed to create an initial backup state!\n");
    269       global_ret = 1;
    270       GNUNET_SCHEDULER_shutdown ();
    271       return;
    272     }
    273     persist_new_state (init_state,
    274                        args[0]);
    275     json_decref (init_state);
    276     GNUNET_SCHEDULER_shutdown ();
    277     return;
    278   }
    279 
    280   /* action processing */
    281   {
    282     const char *action = args[0];
    283 
    284     if (NULL == action)
    285     {
    286       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    287                   "You must specify an action as the first argument (or `-b' or `-r')\n");
    288       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    289                   "Example: anastasis-reducer back\n");
    290       global_ret = 1;
    291       GNUNET_SCHEDULER_shutdown ();
    292       return;
    293     }
    294     args++;
    295     if (NULL != input)
    296     {
    297       arguments = json_loads (input,
    298                               JSON_DECODE_ANY,
    299                               &error);
    300       if (NULL == arguments)
    301       {
    302         GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    303                     "Failed to parse arguments on line %u:%u: %s!\n",
    304                     error.line,
    305                     error.column,
    306                     error.text);
    307         global_ret = 1;
    308         GNUNET_SCHEDULER_shutdown ();
    309         return;
    310       }
    311     }
    312     if (NULL != args[0])
    313     {
    314       prev_state = json_load_file (args[0],
    315                                    JSON_DECODE_ANY,
    316                                    &error);
    317       args++;
    318     }
    319     else
    320     {
    321       prev_state = ANASTASIS_CLI_json_load_capped (stdin,
    322                                                    &error);
    323     }
    324     if (NULL == prev_state)
    325     {
    326       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    327                   "Failed to parse initial state on line %u:%u: %s!\n",
    328                   error.line,
    329                   error.column,
    330                   error.text);
    331       global_ret = 1;
    332       GNUNET_SCHEDULER_shutdown ();
    333       return;
    334     }
    335     output_filename = args[0];
    336     /* initialize HTTP client event loop */
    337     ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    338                             &rc);
    339     rc = GNUNET_CURL_gnunet_rc_create (ctx);
    340     ANASTASIS_redux_init (ctx);
    341     /* Expand identity_attributes if -a is given explicitly and we
    342        are at the respective step of the reduction */
    343     if ( (0 == strcasecmp (action,
    344                            "enter_user_attributes")) &&
    345          (NULL != application_id) &&
    346          (NULL != arguments) )
    347     {
    348       json_t *attr = json_object_get (arguments,
    349                                       "identity_attributes");
    350       if (NULL != attr)
    351         GNUNET_assert (0 ==
    352                        json_object_set_new (attr,
    353                                             "application-id",
    354                                             json_string (application_id)));
    355     }
    356     ra = ANASTASIS_redux_action (prev_state,
    357                                  action,
    358                                  arguments,
    359                                  &action_cb,
    360                                  cls);
    361   }
    362 }
    363 
    364 
    365 int
    366 main (int argc,
    367       char *const *argv)
    368 {
    369   /* the available command line options */
    370   struct GNUNET_GETOPT_CommandLineOption options[] = {
    371     GNUNET_GETOPT_option_string ('A',
    372                                  "application",
    373                                  "ID",
    374                                  "set the application ID",
    375                                  &application_id),
    376     GNUNET_GETOPT_option_string ('a',
    377                                  "arguments",
    378                                  "JSON",
    379                                  "pass a JSON string containing arguments to reducer",
    380                                  &input),
    381     GNUNET_GETOPT_option_flag ('b',
    382                                "backup",
    383                                "use reducer to handle states for backup process",
    384                                &b_flag),
    385     GNUNET_GETOPT_option_flag ('r',
    386                                "restore",
    387                                "use reducer to handle states for restore process",
    388                                &r_flag),
    389     GNUNET_GETOPT_OPTION_END
    390   };
    391   enum GNUNET_GenericReturnValue ret;
    392 
    393   ret = GNUNET_PROGRAM_run (ANASTASIS_project_data (),
    394                             argc,
    395                             argv,
    396                             "anastasis-reducer",
    397                             "This is an application for using Anastasis to handle the states.\n",
    398                             options,
    399                             &run,
    400                             NULL);
    401   GNUNET_free (application_id);
    402   if (GNUNET_SYSERR == ret)
    403     return 3;
    404   if (GNUNET_NO == ret)
    405     return 0;
    406   return global_ret;
    407 }
    408 
    409 
    410 /* end of anastasis-cli-redux.c */