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 */