LCOV - code coverage report
Current view: top level - auditor - taler-auditor-sync.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 126 193 65.3 %
Date: 2025-06-05 21:03:14 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2020-2022 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER 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 General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-auditor-sync.c
      18             :  * @brief Tool used by the auditor to make a 'safe' copy of the exchanges' database.
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include <platform.h>
      22             : #include "taler_exchangedb_lib.h"
      23             : 
      24             : 
      25             : /**
      26             :  * Handle to access the exchange's source database.
      27             :  */
      28             : static struct TALER_EXCHANGEDB_Plugin *src;
      29             : 
      30             : /**
      31             :  * Handle to access the exchange's destination database.
      32             :  */
      33             : static struct TALER_EXCHANGEDB_Plugin *dst;
      34             : 
      35             : /**
      36             :  * Return value from #main().
      37             :  */
      38             : static int global_ret;
      39             : 
      40             : /**
      41             :  * Main task to do synchronization.
      42             :  */
      43             : static struct GNUNET_SCHEDULER_Task *sync_task;
      44             : 
      45             : /**
      46             :  * What is our target transaction size (number of records)?
      47             :  */
      48             : static unsigned int transaction_size = 512;
      49             : 
      50             : /**
      51             :  * Number of records copied in this transaction.
      52             :  */
      53             : static unsigned long long actual_size;
      54             : 
      55             : /**
      56             :  * Terminate once synchronization is achieved.
      57             :  */
      58             : static int exit_if_synced;
      59             : 
      60             : 
      61             : /**
      62             :  * Information we track per replicated table.
      63             :  */
      64             : struct Table
      65             : {
      66             :   /**
      67             :    * Which table is this record about?
      68             :    */
      69             :   enum TALER_EXCHANGEDB_ReplicatedTable rt;
      70             : 
      71             :   /**
      72             :    * Up to which record is the destination table synchronized.
      73             :    */
      74             :   uint64_t start_serial;
      75             : 
      76             :   /**
      77             :    * Highest serial in the source table.
      78             :    */
      79             :   uint64_t end_serial;
      80             : 
      81             :   /**
      82             :    * Marker for the end of the list of #tables.
      83             :    */
      84             :   bool end;
      85             : };
      86             : 
      87             : 
      88             : /**
      89             :  * Information about replicated tables.
      90             :  */
      91             : static struct Table tables[] = {
      92             :   { .rt = TALER_EXCHANGEDB_RT_DENOMINATIONS},
      93             :   { .rt = TALER_EXCHANGEDB_RT_DENOMINATION_REVOCATIONS},
      94             :   { .rt = TALER_EXCHANGEDB_RT_KYC_TARGETS},
      95             :   { .rt = TALER_EXCHANGEDB_RT_WIRE_TARGETS},
      96             :   { .rt = TALER_EXCHANGEDB_RT_LEGITIMIZATION_MEASURES},
      97             :   { .rt = TALER_EXCHANGEDB_RT_LEGITIMIZATION_OUTCOMES},
      98             :   { .rt = TALER_EXCHANGEDB_RT_LEGITIMIZATION_PROCESSES},
      99             :   { .rt = TALER_EXCHANGEDB_RT_RESERVES},
     100             :   { .rt = TALER_EXCHANGEDB_RT_RESERVES_IN},
     101             :   { .rt = TALER_EXCHANGEDB_RT_RESERVES_CLOSE},
     102             :   { .rt = TALER_EXCHANGEDB_RT_RESERVES_OPEN_REQUESTS},
     103             :   { .rt = TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS},
     104             :   { .rt = TALER_EXCHANGEDB_RT_WITHDRAW},
     105             :   { .rt = TALER_EXCHANGEDB_RT_AUDITORS},
     106             :   { .rt = TALER_EXCHANGEDB_RT_AUDITOR_DENOM_SIGS},
     107             :   { .rt = TALER_EXCHANGEDB_RT_EXCHANGE_SIGN_KEYS},
     108             :   { .rt = TALER_EXCHANGEDB_RT_SIGNKEY_REVOCATIONS},
     109             :   { .rt = TALER_EXCHANGEDB_RT_KNOWN_COINS},
     110             :   { .rt = TALER_EXCHANGEDB_RT_REFRESH},
     111             :   { .rt = TALER_EXCHANGEDB_RT_BATCH_DEPOSITS},
     112             :   { .rt = TALER_EXCHANGEDB_RT_COIN_DEPOSITS},
     113             :   { .rt = TALER_EXCHANGEDB_RT_REFUNDS},
     114             :   { .rt = TALER_EXCHANGEDB_RT_WIRE_OUT},
     115             :   { .rt = TALER_EXCHANGEDB_RT_AGGREGATION_TRACKING},
     116             :   { .rt = TALER_EXCHANGEDB_RT_WIRE_FEE},
     117             :   { .rt = TALER_EXCHANGEDB_RT_GLOBAL_FEE},
     118             :   { .rt = TALER_EXCHANGEDB_RT_RECOUP},
     119             :   { .rt = TALER_EXCHANGEDB_RT_RECOUP_REFRESH },
     120             :   { .rt = TALER_EXCHANGEDB_RT_EXTENSIONS},
     121             :   { .rt = TALER_EXCHANGEDB_RT_POLICY_DETAILS },
     122             :   { .rt = TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS },
     123             :   { .rt = TALER_EXCHANGEDB_RT_PURSE_REQUESTS},
     124             :   { .rt = TALER_EXCHANGEDB_RT_PURSE_DECISION},
     125             :   { .rt = TALER_EXCHANGEDB_RT_PURSE_MERGES},
     126             :   { .rt = TALER_EXCHANGEDB_RT_PURSE_DEPOSITS},
     127             :   { .rt = TALER_EXCHANGEDB_RT_ACCOUNT_MERGES},
     128             :   { .rt = TALER_EXCHANGEDB_RT_HISTORY_REQUESTS},
     129             :   { .rt = TALER_EXCHANGEDB_RT_CLOSE_REQUESTS},
     130             :   { .rt = TALER_EXCHANGEDB_RT_WADS_OUT},
     131             :   { .rt = TALER_EXCHANGEDB_RT_WADS_OUT_ENTRIES},
     132             :   { .rt = TALER_EXCHANGEDB_RT_WADS_IN},
     133             :   { .rt = TALER_EXCHANGEDB_RT_WADS_IN_ENTRIES},
     134             :   { .rt = TALER_EXCHANGEDB_RT_PROFIT_DRAINS},
     135             :   { .end = true }
     136             : };
     137             : 
     138             : 
     139             : /**
     140             :  * Closure for #do_insert.
     141             :  */
     142             : struct InsertContext
     143             : {
     144             :   /**
     145             :    * Table we are replicating.
     146             :    */
     147             :   struct Table *table;
     148             : 
     149             :   /**
     150             :    * Set to error if insertion created an error.
     151             :    */
     152             :   enum GNUNET_DB_QueryStatus qs;
     153             : };
     154             : 
     155             : 
     156             : /**
     157             :  * Function called on data to replicate in the auditor's database.
     158             :  *
     159             :  * @param cls closure, a `struct InsertContext`
     160             :  * @param td record from an exchange table
     161             :  * @return #GNUNET_OK to continue to iterate,
     162             :  *         #GNUNET_SYSERR to fail with an error
     163             :  */
     164             : static enum GNUNET_GenericReturnValue
     165         882 : do_insert (void *cls,
     166             :            const struct TALER_EXCHANGEDB_TableData *td)
     167             : {
     168         882 :   struct InsertContext *ctx = cls;
     169             :   enum GNUNET_DB_QueryStatus qs;
     170             : 
     171         882 :   if (0 >= ctx->qs)
     172           0 :     return GNUNET_SYSERR;
     173         882 :   qs = dst->insert_records_by_table (dst->cls,
     174             :                                      td);
     175         882 :   if (0 >= qs)
     176             :   {
     177           0 :     switch (qs)
     178             :     {
     179           0 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     180           0 :       GNUNET_assert (0);
     181             :       break;
     182           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     183           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     184             :                   "Failed to insert record into table %d: no change\n",
     185             :                   td->table);
     186           0 :       break;
     187           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     188           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     189             :                   "Serialization error inserting record into table %d (will retry)\n",
     190             :                   td->table);
     191           0 :       break;
     192           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     193           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     194             :                   "Failed to insert record into table %d: hard error\n",
     195             :                   td->table);
     196           0 :       break;
     197             :     }
     198           0 :     ctx->qs = qs;
     199           0 :     return GNUNET_SYSERR;
     200             :   }
     201         882 :   actual_size++;
     202         882 :   ctx->table->start_serial = td->serial;
     203         882 :   return GNUNET_OK;
     204             : }
     205             : 
     206             : 
     207             : /**
     208             :  * Run one replication transaction.
     209             :  *
     210             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR to rollback
     211             :  */
     212             : static enum GNUNET_GenericReturnValue
     213           2 : transact (void)
     214             : {
     215           2 :   struct InsertContext ctx = {
     216             :     .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
     217             :   };
     218             : 
     219           2 :   if (0 >
     220           2 :       src->start (src->cls,
     221             :                   "lookup src serials"))
     222           0 :     return GNUNET_SYSERR;
     223          88 :   for (unsigned int i = 0; ! tables[i].end; i++)
     224          86 :     src->lookup_serial_by_table (src->cls,
     225             :                                  tables[i].rt,
     226             :                                  &tables[i].end_serial);
     227           2 :   src->rollback (src->cls);
     228           2 :   if (GNUNET_OK !=
     229           2 :       dst->start (dst->cls,
     230             :                   "lookup dst serials"))
     231           0 :     return GNUNET_SYSERR;
     232          88 :   for (unsigned int i = 0; ! tables[i].end; i++)
     233          86 :     dst->lookup_serial_by_table (dst->cls,
     234             :                                  tables[i].rt,
     235             :                                  &tables[i].start_serial);
     236           2 :   dst->rollback (dst->cls);
     237          88 :   for (unsigned int i = 0; ! tables[i].end; i++)
     238             :   {
     239          86 :     struct Table *table = &tables[i];
     240             : 
     241          86 :     if (table->start_serial == table->end_serial)
     242          70 :       continue;
     243          16 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     244             :                 "Replicating table %d from %llu to %llu\n",
     245             :                 i,
     246             :                 (unsigned long long) table->start_serial,
     247             :                 (unsigned long long) table->end_serial);
     248          16 :     ctx.table = table;
     249          32 :     while (table->start_serial < table->end_serial)
     250             :     {
     251             :       enum GNUNET_DB_QueryStatus qs;
     252             : 
     253          16 :       if (GNUNET_OK !=
     254          16 :           src->start (src->cls,
     255             :                       "copy table (src)"))
     256           0 :         return GNUNET_SYSERR;
     257          16 :       if (GNUNET_OK !=
     258          16 :           dst->start (dst->cls,
     259             :                       "copy table (dst)"))
     260           0 :         return GNUNET_SYSERR;
     261          16 :       qs = src->lookup_records_by_table (src->cls,
     262             :                                          table->rt,
     263             :                                          table->start_serial,
     264             :                                          &do_insert,
     265             :                                          &ctx);
     266          16 :       if (ctx.qs < 0)
     267           0 :         qs = ctx.qs;
     268          16 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     269             :       {
     270           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     271             :                     "Failed to lookup records from table %d: hard error\n",
     272             :                     i);
     273           0 :         global_ret = EXIT_FAILURE;
     274           0 :         return GNUNET_SYSERR;
     275             :       }
     276          16 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     277             :       {
     278           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     279             :                     "Serialization error looking up records from table %d (will retry)\n",
     280             :                     i);
     281           0 :         return GNUNET_SYSERR; /* will retry */
     282             :       }
     283          16 :       if (0 == qs)
     284             :       {
     285           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     286             :                     "Failed to lookup records from table %d: no results\n",
     287             :                     i);
     288           0 :         GNUNET_break (0); /* should be impossible */
     289           0 :         global_ret = EXIT_FAILURE;
     290           0 :         return GNUNET_SYSERR;
     291             :       }
     292          16 :       if (0 == ctx.qs)
     293           0 :         return GNUNET_SYSERR; /* insertion failed, maybe record existed? try again */
     294          16 :       src->rollback (src->cls);
     295          16 :       qs = dst->commit (dst->cls);
     296          16 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     297             :       {
     298           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     299             :                     "Serialization error committing transaction on table %d (will retry)\n",
     300             :                     i);
     301           0 :         continue;
     302             :       }
     303          16 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     304             :       {
     305           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     306             :                     "Hard error committing transaction on table %d\n",
     307             :                     i);
     308           0 :         global_ret = EXIT_FAILURE;
     309           0 :         return GNUNET_SYSERR;
     310             :       }
     311             :     }
     312             :   }
     313             :   /* we do not care about conflicting UPDATEs to src table, so safe to just rollback */
     314           2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     315             :               "Sync pass completed successfully with %llu updates\n",
     316             :               actual_size);
     317           2 :   return GNUNET_OK;
     318             : }
     319             : 
     320             : 
     321             : /**
     322             :  * Task to do the actual synchronization work.
     323             :  *
     324             :  * @param cls NULL, unused
     325             :  */
     326             : static void
     327           2 : do_sync (void *cls)
     328             : {
     329             :   static struct GNUNET_TIME_Relative delay;
     330             : 
     331             :   (void) cls;
     332           2 :   sync_task = NULL;
     333           2 :   actual_size = 0;
     334           2 :   if (GNUNET_SYSERR ==
     335           2 :       src->preflight (src->cls))
     336             :   {
     337           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     338             :                 "Failed to begin transaction with data source. Exiting\n");
     339           0 :     return;
     340             :   }
     341           2 :   if (GNUNET_SYSERR ==
     342           2 :       dst->preflight (dst->cls))
     343             :   {
     344           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     345             :                 "Failed to begin transaction with data destination. Exiting\n");
     346           0 :     return;
     347             :   }
     348           2 :   if (GNUNET_OK != transact ())
     349             :   {
     350           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     351             :                 "Transaction failed, rolling back\n");
     352           0 :     src->rollback (src->cls);
     353           0 :     dst->rollback (dst->cls);
     354             :   }
     355           2 :   if (0 != global_ret)
     356             :   {
     357           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     358             :                 "Transaction failed permanently, exiting\n");
     359           0 :     return;
     360             :   }
     361           2 :   if ( (0 == actual_size) &&
     362             :        (exit_if_synced) )
     363             :   {
     364           1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     365             :                 "Databases are synchronized. Exiting\n");
     366           1 :     return;
     367             :   }
     368           1 :   if (actual_size < transaction_size / 2)
     369             :   {
     370           0 :     delay = GNUNET_TIME_STD_BACKOFF (delay);
     371             :   }
     372           1 :   else if (actual_size >= transaction_size)
     373             :   {
     374           1 :     delay = GNUNET_TIME_UNIT_ZERO;
     375             :   }
     376           1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     377             :               "Next sync pass in %s\n",
     378             :               GNUNET_STRINGS_relative_time_to_string (delay,
     379             :                                                       GNUNET_YES));
     380           1 :   sync_task = GNUNET_SCHEDULER_add_delayed (delay,
     381             :                                             &do_sync,
     382             :                                             NULL);
     383             : }
     384             : 
     385             : 
     386             : /**
     387             :  * Set an option of type 'char *' from the command line with
     388             :  * filename expansion a la #GNUNET_STRINGS_filename_expand().
     389             :  *
     390             :  * @param ctx command line processing context
     391             :  * @param scls additional closure (will point to the `char *`,
     392             :  *             which will be allocated)
     393             :  * @param option name of the option
     394             :  * @param value actual value of the option (a string)
     395             :  * @return #GNUNET_OK
     396             :  */
     397             : static enum GNUNET_GenericReturnValue
     398           2 : set_filename (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
     399             :               void *scls,
     400             :               const char *option,
     401             :               const char *value)
     402             : {
     403           2 :   char **val = scls;
     404             : 
     405             :   (void) ctx;
     406             :   (void) option;
     407           2 :   GNUNET_assert (NULL != value);
     408           2 :   GNUNET_free (*val);
     409           2 :   *val = GNUNET_STRINGS_filename_expand (value);
     410           2 :   return GNUNET_OK;
     411             : }
     412             : 
     413             : 
     414             : /**
     415             :  * Allow user to specify configuration file name (-s option)
     416             :  *
     417             :  * @param[out] fn set to the name of the configuration file
     418             :  */
     419             : static struct GNUNET_GETOPT_CommandLineOption
     420           1 : option_cfgfile_src (char **fn)
     421             : {
     422           1 :   struct GNUNET_GETOPT_CommandLineOption clo = {
     423             :     .shortName = 's',
     424             :     .name = "source-configuration",
     425             :     .argumentHelp = "FILENAME",
     426             :     .description = gettext_noop (
     427             :       "use configuration file FILENAME for the SOURCE database"),
     428             :     .require_argument = 1,
     429             :     .processor = &set_filename,
     430             :     .scls = (void *) fn
     431             :   };
     432             : 
     433           1 :   return clo;
     434             : }
     435             : 
     436             : 
     437             : /**
     438             :  * Allow user to specify configuration file name (-d option)
     439             :  *
     440             :  * @param[out] fn set to the name of the configuration file
     441             :  */
     442             : static struct GNUNET_GETOPT_CommandLineOption
     443           1 : option_cfgfile_dst (char **fn)
     444             : {
     445           1 :   struct GNUNET_GETOPT_CommandLineOption clo = {
     446             :     .shortName = 'd',
     447             :     .name = "destination-configuration",
     448             :     .argumentHelp = "FILENAME",
     449             :     .description = gettext_noop (
     450             :       "use configuration file FILENAME for the DESTINATION database"),
     451             :     .require_argument = 1,
     452             :     .processor = &set_filename,
     453             :     .scls = (void *) fn
     454             :   };
     455             : 
     456           1 :   return clo;
     457             : }
     458             : 
     459             : 
     460             : static struct GNUNET_CONFIGURATION_Handle *
     461           2 : load_config (const char *cfgfile)
     462             : {
     463             :   struct GNUNET_CONFIGURATION_Handle *cfg;
     464             : 
     465           2 :   cfg = GNUNET_CONFIGURATION_create (TALER_AUDITOR_project_data ());
     466           2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     467             :               "Loading config file: %s\n",
     468             :               cfgfile);
     469           2 :   if (GNUNET_SYSERR ==
     470           2 :       GNUNET_CONFIGURATION_load (cfg,
     471             :                                  cfgfile))
     472             :   {
     473           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     474             :                 "Malformed configuration file `%s', exit ...\n",
     475             :                 cfgfile);
     476           0 :     GNUNET_CONFIGURATION_destroy (cfg);
     477           0 :     return NULL;
     478             :   }
     479           2 :   return cfg;
     480             : }
     481             : 
     482             : 
     483             : /**
     484             :  * Shutdown task.
     485             :  *
     486             :  * @param cls NULL, unused
     487             :  */
     488             : static void
     489           1 : do_shutdown (void *cls)
     490             : {
     491             :   (void) cls;
     492           1 :   if (NULL != sync_task)
     493             :   {
     494           0 :     GNUNET_SCHEDULER_cancel (sync_task);
     495           0 :     sync_task = NULL;
     496             :   }
     497           1 : }
     498             : 
     499             : 
     500             : /**
     501             :  * Initial task.
     502             :  *
     503             :  * @param cls NULL, unused
     504             :  */
     505             : static void
     506           1 : run (void *cls)
     507             : {
     508             :   (void) cls;
     509             : 
     510           1 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
     511             :                                  NULL);
     512           1 :   sync_task = GNUNET_SCHEDULER_add_now (&do_sync,
     513             :                                         NULL);
     514           1 : }
     515             : 
     516             : 
     517             : /**
     518             :  * Setup plugins in #src and #dst and #run() the main
     519             :  * logic with those plugins.
     520             :  */
     521             : static void
     522           1 : setup (struct GNUNET_CONFIGURATION_Handle *src_cfg,
     523             :        struct GNUNET_CONFIGURATION_Handle *dst_cfg)
     524             : {
     525           1 :   src = TALER_EXCHANGEDB_plugin_load (src_cfg,
     526             :                                       false);
     527           1 :   if (NULL == src)
     528             :   {
     529           0 :     global_ret = EXIT_NOTINSTALLED;
     530           0 :     return;
     531             :   }
     532           1 :   dst = TALER_EXCHANGEDB_plugin_load (dst_cfg,
     533             :                                       false);
     534           1 :   if (NULL == dst)
     535             :   {
     536           0 :     global_ret = EXIT_NOTINSTALLED;
     537           0 :     TALER_EXCHANGEDB_plugin_unload (src);
     538           0 :     src = NULL;
     539           0 :     return;
     540             :   }
     541           1 :   GNUNET_SCHEDULER_run (&run,
     542             :                         NULL);
     543           1 :   TALER_EXCHANGEDB_plugin_unload (src);
     544           1 :   src = NULL;
     545           1 :   TALER_EXCHANGEDB_plugin_unload (dst);
     546           1 :   dst = NULL;
     547             : }
     548             : 
     549             : 
     550             : /**
     551             :  * The main function of the taler-auditor-exchange tool.  This tool is used
     552             :  * to add (or remove) an exchange's master key and base URL to the auditor's
     553             :  * database.
     554             :  *
     555             :  * @param argc number of arguments from the command line
     556             :  * @param argv command line arguments
     557             :  * @return 0 ok, non-zero on error
     558             :  */
     559             : int
     560           1 : main (int argc,
     561             :       char *const *argv)
     562             : {
     563           1 :   char *src_cfgfile = NULL;
     564           1 :   char *dst_cfgfile = NULL;
     565           1 :   char *level = GNUNET_strdup ("WARNING");
     566             :   struct GNUNET_CONFIGURATION_Handle *src_cfg;
     567             :   struct GNUNET_CONFIGURATION_Handle *dst_cfg;
     568           1 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
     569           1 :     GNUNET_GETOPT_option_mandatory (
     570             :       option_cfgfile_src (&src_cfgfile)),
     571           1 :     GNUNET_GETOPT_option_mandatory (
     572             :       option_cfgfile_dst (&dst_cfgfile)),
     573           1 :     GNUNET_GETOPT_option_help (
     574             :       TALER_AUDITOR_project_data (),
     575             :       gettext_noop ("Make a safe copy of an exchange database")),
     576           1 :     GNUNET_GETOPT_option_uint (
     577             :       'b',
     578             :       "batch",
     579             :       "SIZE",
     580             :       gettext_noop (
     581             :         "target SIZE for a the number of records to copy in one transaction"),
     582             :       &transaction_size),
     583           1 :     GNUNET_GETOPT_option_flag (
     584             :       't',
     585             :       "terminate-when-synchronized",
     586             :       gettext_noop (
     587             :         "terminate as soon as the databases are synchronized"),
     588             :       &exit_if_synced),
     589           1 :     GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
     590           1 :     GNUNET_GETOPT_option_loglevel (&level),
     591             :     GNUNET_GETOPT_OPTION_END
     592             :   };
     593             : 
     594           1 :   TALER_gcrypt_init (); /* must trigger initialization manually at this point! */
     595             :   {
     596             :     int ret;
     597             : 
     598           1 :     ret = GNUNET_GETOPT_run ("taler-auditor-sync",
     599             :                              options,
     600             :                              argc, argv);
     601           1 :     if (GNUNET_NO == ret)
     602           0 :       return EXIT_SUCCESS;
     603           1 :     if (GNUNET_SYSERR == ret)
     604           0 :       return EXIT_INVALIDARGUMENT;
     605             :   }
     606           1 :   GNUNET_assert (GNUNET_OK ==
     607             :                  GNUNET_log_setup ("taler-auditor-sync",
     608             :                                    level,
     609             :                                    NULL));
     610           1 :   GNUNET_free (level);
     611             :   /* suppress compiler warnings... */
     612           1 :   GNUNET_assert (NULL != src_cfgfile);
     613           1 :   GNUNET_assert (NULL != dst_cfgfile);
     614           1 :   if (0 == strcmp (src_cfgfile,
     615             :                    dst_cfgfile))
     616             :   {
     617           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     618             :                 "Source and destination configuration files must differ!\n");
     619           0 :     return EXIT_INVALIDARGUMENT;
     620             :   }
     621           1 :   src_cfg = load_config (src_cfgfile);
     622           1 :   if (NULL == src_cfg)
     623             :   {
     624           0 :     GNUNET_free (src_cfgfile);
     625           0 :     GNUNET_free (dst_cfgfile);
     626           0 :     return EXIT_NOTCONFIGURED;
     627             :   }
     628           1 :   dst_cfg = load_config (dst_cfgfile);
     629           1 :   if (NULL == dst_cfg)
     630             :   {
     631           0 :     GNUNET_CONFIGURATION_destroy (src_cfg);
     632           0 :     GNUNET_free (src_cfgfile);
     633           0 :     GNUNET_free (dst_cfgfile);
     634           0 :     return EXIT_NOTCONFIGURED;
     635             :   }
     636           1 :   setup (src_cfg,
     637             :          dst_cfg);
     638           1 :   GNUNET_CONFIGURATION_destroy (src_cfg);
     639           1 :   GNUNET_CONFIGURATION_destroy (dst_cfg);
     640           1 :   GNUNET_free (src_cfgfile);
     641           1 :   GNUNET_free (dst_cfgfile);
     642             : 
     643           1 :   return global_ret;
     644             : }
     645             : 
     646             : 
     647             : /* end of taler-auditor-sync.c */

Generated by: LCOV version 1.16