LCOV - code coverage report
Current view: top level - exchange - taler-exchange-wirewatch.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 69 120 57.5 %
Date: 2017-09-17 17:24:28 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2016, 2017 GNUnet e.V.
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero 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 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             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : 
      17             : /**
      18             :  * @file taler-exchange-wirewatch.c
      19             :  * @brief Process that watches for wire transfers to the exchange's bank account
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <gnunet/gnunet_util_lib.h>
      24             : #include <jansson.h>
      25             : #include <pthread.h>
      26             : #include <microhttpd.h>
      27             : #include "taler_exchangedb_lib.h"
      28             : #include "taler_exchangedb_plugin.h"
      29             : #include "taler_json_lib.h"
      30             : #include "taler_wire_lib.h"
      31             : 
      32             : /**
      33             :  * How long do we sleep before trying again if there
      34             :  * are no transactions returned by the wire plugin?
      35             :  */
      36             : #define DELAY GNUNET_TIME_UNIT_SECONDS
      37             : 
      38             : 
      39             : /**
      40             :  * Handle to the plugin.
      41             :  */
      42             : static struct TALER_WIRE_Plugin *wire_plugin;
      43             : 
      44             : /**
      45             :  * Which currency is used by this exchange?
      46             :  */
      47             : static char *exchange_currency_string;
      48             : 
      49             : /**
      50             :  * The exchange's configuration (global)
      51             :  */
      52             : static const struct GNUNET_CONFIGURATION_Handle *cfg;
      53             : 
      54             : /**
      55             :  * Our DB plugin.
      56             :  */
      57             : static struct TALER_EXCHANGEDB_Plugin *db_plugin;
      58             : 
      59             : /**
      60             :  * Value to return from main(). #GNUNET_OK on success, #GNUNET_SYSERR
      61             :  * on serious errors.
      62             :  */
      63             : static int global_ret;
      64             : 
      65             : /**
      66             :  * Encoded offset in the wire transfer list that we
      67             :  * processed last.
      68             :  */
      69             : static void *last_row_off;
      70             : 
      71             : /**
      72             :  * Number of bytes in #last_row_off.
      73             :  */
      74             : static size_t last_row_off_size;
      75             : 
      76             : /**
      77             :  * Encoded offset in the wire transfer list from where
      78             :  * to start the next query with the bank.
      79             :  */
      80             : static void *start_off;
      81             : 
      82             : /**
      83             :  * Number of bytes in #start_off.
      84             :  */
      85             : static size_t start_off_size;
      86             : 
      87             : /**
      88             :  * Which wire plugin are we watching?
      89             :  */
      90             : static char *type;
      91             : 
      92             : /**
      93             :  * Should we delay the next request to the wire plugin a bit?
      94             :  */
      95             : static int delay;
      96             : 
      97             : /**
      98             :  * Are we run in testing mode and should only do one pass?
      99             :  */
     100             : static int test_mode;
     101             : 
     102             : /**
     103             :  * Next task to run, if any.
     104             :  */
     105             : static struct GNUNET_SCHEDULER_Task *task;
     106             : 
     107             : /**
     108             :  * Active request for history.
     109             :  */
     110             : static struct TALER_WIRE_HistoryHandle *hh;
     111             : 
     112             : 
     113             : /**
     114             :  * We're being aborted with CTRL-C (or SIGTERM). Shut down.
     115             :  *
     116             :  * @param cls closure
     117             :  */
     118             : static void
     119           2 : shutdown_task (void *cls)
     120             : {
     121           2 :   if (NULL != task)
     122             :   {
     123           0 :     GNUNET_SCHEDULER_cancel (task);
     124           0 :     task = NULL;
     125             :   }
     126           2 :   if (NULL != hh)
     127             :   {
     128           0 :     wire_plugin->get_history_cancel (wire_plugin->cls,
     129             :                                      hh);
     130           0 :     hh = NULL;
     131             :   }
     132           2 :   TALER_EXCHANGEDB_plugin_unload (db_plugin);
     133           2 :   db_plugin = NULL;
     134           2 :  TA\EŇ_WIRE_plugin_unload (wire_plugin);
     135           2 :   wire_plugin = NULL;
     136           2 :   GNUNET_free_non_null (start_off);
     137           2 :   start_off = NULL;
     138           2 : }
     139             : 
     140             : 
     141             : /**
     142             :  * Parse configuration parameters for the exchange server into the
     143             :  * corresponding global variables.
     144             :  *
     145             :  * @return #GNUNET_OK on success
     146             :  */
     147             : static int
     148           2 : exchange_serve_process_config ()
     149             : {
     150           2 :   if (NULL == type)
     151             :   {
     152           0 :     fprintf (stderr,
     153             :              "Option `-t' to specify wire plugin is mandatory.\n");
     154           0 :     return GNUNET_SYSERR;
     155             :   }
     156           2 :   if (GNUNET_OK !=
     157           2 :       GNUNET_CONFIGURATION_get_value_string (cfg,
     158             :                                              "taler",
     159             :                                              "currency",
     160             :                                              &exchange_currency_string))
     161             :   {
     162           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     163             :                                "taler",
     164             :                                "currency");
     165           0 :     return GNUNET_SYSERR;
     166             :   }
     167           2 :   if (strlen (exchange_currency_string) >= TALER_CURRENCY_LEN)
     168             :   {
     169           0 :     fprintf (stderr,
     170             :              "Currency `%s' longer than the allowed limit of %u characters.",
     171             :              exchange_currency_string,
     172             :              (unsigned int) TALER_CURRENCY_LEN);
     173           0 :     return GNUNET_SYSERR;
     174             :   }
     175             : 
     176           2 :   if (NULL ==
     177           2 :       (db_plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
     178             :   {
     179           0 :     fprintf (stderr,
     180             :              "Failed to initialize DB subsystem\n");
     181           0 :     return GNUNET_SYSERR;
     182             :   }
     183           2 :   if (NULL ==
     184           2 :       (wire_plugin = TALER_WIRE_plugin_load (cfg,
     185             :                                              type)))
     186             :   {
     187           0 :     fprintf (stderr,
     188             :              "Failed to load wire plugin for `%s'\n",
     189             :              type);
     190           0 :     TALER_EXCHANGEDB_plugin_unload (db_plugin);
     191           0 :     return GNUNET_SYSERR;
     192             :   }
     193             : 
     194           2 :   return GNUNET_OK;
     195             : }
     196             : 
     197             : 
     198             : /**
     199             :  * Query for incoming wire transfers.
     200             :  *
     201             :  * @param cls NULL
     202             :  */
     203             : static void
     204             : find_transfers (void *cls);
     205             : 
     206             : 
     207             : /**
     208             :  * Callbacks of this type are used to serve the result of asking
     209             :  * the bank for the transaction history.
     210             :  *
     211             :  * @param cls closure with the `struct TALER_EXCHANGEDB_Session *`
     212             :  * @param dir direction of the transfer
     213             :  * @param row_off identification of the position at which we are querying
     214             :  * @param row_off_size number of bytes in @a row_off
     215             :  * @param details details about the wire transfer
     216             :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
     217             :  */
     218             : static int
     219           3 : history_cb (void *cls,
     220             :             enum TALER_BANK_Direction dir,
     221             :             const void *row_off,
     222             :             size_t row_off_size,
     223             :             const struct TALER_WIRE_TransferDetails *details)
     224             : {
     225           3 :   struct TALER_EXCHANGEDB_Session *session = cls;
     226             :   enum GNUNET_DB_QueryStatus qs;
     227             : 
     228           3 :   if (TALER_BANK_DIRECTION_NONE == dir)
     229             :   {
     230           2 :     hh = NULL;
     231             : 
     232           2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     233             :                 "End of list. Committing progress!\n");
     234           2 :     qs = db_plugin->commit (db_plugin->cls,
     235             :                             session);
     236           2 :     if (0 <= qs)
     237             :     {
     238           2 :       GNUNET_free_non_null (start_off);
     239           2 :       start_off = last_row_off;
     240           2 :       start_off_size = last_row_off_size;
     241             :     }
     242           2 :     if ( (GNUNET_YES == delay) &&
     243             :          (test_mode) )
     244             :     {
     245           2 :       GNUNET_SCHEDULER_shutdown ();
     246           2 :       return GNUNET_OK;
     247             :     }
     248           0 :     if (GNUNET_YES == delay)
     249           0 :       task = GNUNET_SCHEDULER_add_delayed (DELAY,
     250             :                                            &find_transfers,
     251             :                                            NULL);
     252             :     else
     253           0 :       task = GNUNET_SCHEDULER_add_now (&find_transfers,
     254             :                                        NULL);
     255           0 :     return GNUNET_OK; /* will be ignored anyway */
     256             :   }
     257           1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     258             :               "Adding wire transfer over %s with subject `%s'\n",
     259             :               TALER_amount2s (&details->amount),
     260             :               TALER_B2S (&details->reserve_pub));
     261           2 :   qs = db_plugin->reserves_in_insert (db_plugin->cls,
     262             :                                       session,
     263             :                                       &details->reserve_pub,
     264             :                                       &details->amount,
     265             :                                       details->execution_date,
     266           1 :                                       details->account_details,
     267             :                                       row_off,
     268             :                                       row_off_size);
     269           1 :   if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     270             :   {
     271           0 :     GNUNET_break (0);
     272           0 :     db_plugin->rollback (db_plugin->cls,
     273             :                          session);
     274           0 :     GNUNET_SCHEDULER_shutdown ();
     275           0 :     return GNUNET_SYSERR;
     276             :   }
     277           1 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     278             :   {
     279           0 :     db_plugin->rollback (db_plugin->cls,
     280             :                          session);
     281             :     /* try again */
     282           0 :     task = GNUNET_SCHEDULER_add_now (&find_transfers,
     283             :                                      NULL);
     284           0 :     return GNUNET_SYSERR;
     285             :   }
     286             : 
     287           1 :   if (last_row_off_size != row_off_size)
     288             :   {
     289           1 :     GNUNET_free_non_null (last_row_off);
     290           1 :     last_row_off = GNUNET_malloc (row_off_size);
     291             :   }
     292           1 :   memcpy (last_row_off,
     293             :           row_off,
     294             :           row_off_size);
     295           1 :   return GNUNET_OK;
     296             : }
     297             : 
     298             : 
     299             : /**
     300             :  * Query for incoming wire transfers.
     301             :  *
     302             :  * @param cls NULL
     303             :  */
     304             : static void
     305           2 : find_transfers (void *cls)
     306             : {
     307             :   struct TALER_EXCHANGEDB_Session *session;
     308             :   enum GNUNET_DB_QueryStatus qs;
     309             : 
     310           2 :   task = NULL;
     311           2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     312             :               "Checking for incoming wire transfers\n");
     313             : 
     314           2 :   if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
     315             :   {
     316           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     317             :                 "Failed to obtain database session!\n");
     318           0 :     global_ret = GNUNET_SYSERR;
     319           0 :     GNUNET_SCHEDULER_shutdown ();
     320           0 :     return;
     321             :   }
     322           2 :   if (GNUNET_OK !=
     323           2 :       db_plugin->start (db_plugin->cls,
     324             :                         session))
     325             :   {
     326           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     327             :                 "Failed to start database transaction!\n");
     328           0 :     global_ret = GNUNET_SYSERR;
     329           0 :     GNUNET_SCHEDULER_shutdown ();
     330           0 :     return;
     331             :   }
     332           2 :   qs = db_plugin->get_latest_reserve_in_reference (db_plugin->cls,
     333             :                                                    session,
     334             :                                                    &start_off,
     335             :                                                    &start_off_size);
     336           2 :   if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     337             :   {
     338           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     339             :                 "Failed to obtain starting point for montoring from database!\n");
     340           0 :     global_ret = GNUNET_SYSERR;
     341           0 :     GNUNET_SCHEDULER_shutdown ();
     342           0 :     return;
     343             :   }
     344           2 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     345             :   {
     346             :     /* try again */
     347           0 :     db_plugin->rollback (db_plugin->cls,
     348             :                          session);
     349           0 :     task = GNUNET_SCHEDULER_add_now (&find_transfers,
     350             :                                      NULL);
     351           0 :     return;
     352             :   }
     353           2 :   delay = GNUNET_YES;
     354           2 :   hh = wire_plugin->get_history (wire_plugin->cls,
     355             :                                  TALER_BANK_DIRECTION_CREDIT,
     356             :                                  start_off,
     357             :                                  start_off_size,
     358             :                                  1024,
     359             :                                  &history_cb,
     360             :                                  session);
     361           2 :   if (NULL == hh)
     362             :   {
     363           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     364             :                 "Failed to start request for account history!\n");
     365           0 :     db_plugin->rollback (db_plugin->cls,
     366             :                          session);
     367           0 :     global_ret = GNUNET_SYSERR;
     368           0 :     GNUNET_SCHEDULER_shutdown ();
     369           0 :     return;
     370             :   }
     371             : }
     372             : 
     373             : 
     374             : /**
     375             :  * First task.
     376             :  *
     377             :  * @param cls closure, NULL
     378             :  * @param args remaining command-line arguments
     379             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
     380             :  * @param c configuration
     381             :  */
     382             : static void
     383           2 : run (void *cls,
     384             :      char *const *args,
     385             :      const char *cfgfile,
     386             :      const struct GNUNET_CONFIGURATION_Handle *c)
     387             : {
     388           2 :   cfg = c;
     389           2 :   if (GNUNET_OK !=
     390           2 :       exchange_serve_process_config ())
     391             :   {
     392           0 :     global_ret = 1;
     393           0 :     return;
     394             :   }
     395             : 
     396           2 :   task = GNUNET_SCHEDULER_add_now (&find_transfers,
     397             :                                    NULL);
     398           2 :   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
     399             :                                  cls);
     400             : }
     401             : 
     402             : 
     403             : /**
     404             :  * The main function of taler-exchange-wirewatch
     405             :  *
     406             :  * @param argc number of arguments from the command line
     407             :  * @param argv command line arguments
     408             :  * @return 0 ok, 1 on error
     409             :  */
     410             : int
     411           2 : main (int argc,
     412             :       char *const *argv)
     413             : {
     414           2 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
     415             :     GNUNET_GETOPT_option_string ('t',
     416             :                                  "type",
     417             :                                  "PLUGINNAME",
     418             :                                  "which wire plugin to use",
     419             :                                  &type),
     420             :     GNUNET_GETOPT_option_flag ('T',
     421             :                                "test",
     422             :                                "run in test mode and exit when idle",
     423             :                                &test_mode),
     424             :     GNUNET_GETOPT_OPTION_END
     425             :   };
     426             : 
     427           2 :   if (GNUNET_OK !=
     428           2 :       GNUNET_STRINGS_get_utf8_args (argc, argv,
     429             :                                     &argc, &argv))
     430           0 :     return 2;
     431           2 :   if (GNUNET_OK !=
     432           2 :       GNUNET_PROGRAM_run (argc, argv,
     433             :                           "taler-exchange-wirewatch",
     434             :                           gettext_noop ("background process that watches for incomming wire transfers from customers"),
     435             :                           options,
     436             :                           &run, NULL))
     437             :   {
     438           0 :     GNUNET_free ((void*) argv);
     439           0 :     return 1;
     440             :   }
     441           2 :   GNUNET_free ((void*) argv);
     442           2 :   return global_ret;
     443             : }
     444             : 
     445             : /* end of taler-exchange-wirewatch.c */

Generated by: LCOV version 1.13