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