LCOV - code coverage report
Current view: top level - backend - taler-merchant-wirewatch.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 132 212 62.3 %
Date: 2025-06-23 16:22:09 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2023 Taler Systems SA
       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             :  * @file taler-merchant-wirewatch.c
      18             :  * @brief Process that imports information about incoming bank transfers into the merchant backend
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include "microhttpd.h"
      23             : #include <gnunet/gnunet_util_lib.h>
      24             : #include <jansson.h>
      25             : #include <pthread.h>
      26             : #include <taler/taler_dbevents.h>
      27             : #include "taler_merchant_util.h"
      28             : #include "taler_merchant_bank_lib.h"
      29             : #include "taler_merchantdb_lib.h"
      30             : #include "taler_merchantdb_plugin.h"
      31             : 
      32             : /**
      33             :  * Timeout for the bank interaction.  Rather long as we should do long-polling
      34             :  * and do not want to wake up too often.
      35             :  */
      36             : #define BANK_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, \
      37             :                                                     5)
      38             : 
      39             : 
      40             : /**
      41             :  * Information about a watch job.
      42             :  */
      43             : struct Watch
      44             : {
      45             :   /**
      46             :    * Kept in a DLL.
      47             :    */
      48             :   struct Watch *next;
      49             : 
      50             :   /**
      51             :    * Kept in a DLL.
      52             :    */
      53             :   struct Watch *prev;
      54             : 
      55             :   /**
      56             :    * Next task to run, if any.
      57             :    */
      58             :   struct GNUNET_SCHEDULER_Task *task;
      59             : 
      60             :   /**
      61             :    * Dynamically adjusted long polling time-out.
      62             :    */
      63             :   struct GNUNET_TIME_Relative bank_timeout;
      64             : 
      65             :   /**
      66             :    * For which instance are we importing bank transfers?
      67             :    */
      68             :   char *instance_id;
      69             : 
      70             :   /**
      71             :    * For which account are we importing bank transfers?
      72             :    */
      73             :   struct TALER_FullPayto payto_uri;
      74             : 
      75             :   /**
      76             :    * Bank history request.
      77             :    */
      78             :   struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh;
      79             : 
      80             :   /**
      81             :    * Start row for the bank interaction. Exclusive.
      82             :    */
      83             :   uint64_t start_row;
      84             : 
      85             :   /**
      86             :    * Artificial delay to use between API calls. Used to
      87             :    * throttle on failures.
      88             :    */
      89             :   struct GNUNET_TIME_Relative delay;
      90             : 
      91             :   /**
      92             :    * When did we start our last HTTP request?
      93             :    */
      94             :   struct GNUNET_TIME_Absolute start_time;
      95             : 
      96             :   /**
      97             :    * How long should long-polling take at least?
      98             :    */
      99             :   struct GNUNET_TIME_Absolute long_poll_timeout;
     100             : 
     101             :   /**
     102             :    * Login data for the bank.
     103             :    */
     104             :   struct TALER_MERCHANT_BANK_AuthenticationData ad;
     105             : 
     106             :   /**
     107             :    * Set to true if we found a transaction in the last iteration.
     108             :    */
     109             :   bool found;
     110             : 
     111             : };
     112             : 
     113             : 
     114             : /**
     115             :  * Head of active watches.
     116             :  */
     117             : static struct Watch *w_head;
     118             : 
     119             : /**
     120             :  * Tail of active watches.
     121             :  */
     122             : static struct Watch *w_tail;
     123             : 
     124             : /**
     125             :  * The merchant's configuration.
     126             :  */
     127             : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     128             : 
     129             : /**
     130             :  * Our database plugin.
     131             :  */
     132             : static struct TALER_MERCHANTDB_Plugin *db_plugin;
     133             : 
     134             : /**
     135             :  * Handle to the context for interacting with the bank.
     136             :  */
     137             : static struct GNUNET_CURL_Context *ctx;
     138             : 
     139             : /**
     140             :  * Scheduler context for running the @e ctx.
     141             :  */
     142             : static struct GNUNET_CURL_RescheduleContext *rc;
     143             : 
     144             : /**
     145             :  * Event handler to learn that the configuration changed
     146             :  * and we should shutdown (to be restarted).
     147             :  */
     148             : static struct GNUNET_DB_EventHandler *eh;
     149             : 
     150             : /**
     151             :  * Value to return from main(). 0 on success, non-zero on errors.
     152             :  */
     153             : static int global_ret;
     154             : 
     155             : /**
     156             :  * How many transactions should we fetch at most per batch?
     157             :  */
     158             : static unsigned int batch_size = 32;
     159             : 
     160             : /**
     161             :  * #GNUNET_YES if we are in test mode and should exit when idle.
     162             :  */
     163             : static int test_mode;
     164             : 
     165             : /**
     166             :  * #GNUNET_YES if we are in persistent mode and do
     167             :  * not exit on #config_changed.
     168             :  */
     169             : static int persist_mode;
     170             : 
     171             : /**
     172             :  * Set to true if we are shutting down due to a
     173             :  * configuration change.
     174             :  */
     175             : static bool config_changed_flag;
     176             : 
     177             : /**
     178             :  * Save progress in DB.
     179             :  */
     180             : static void
     181           4 : save (struct Watch *w)
     182             : {
     183             :   enum GNUNET_DB_QueryStatus qs;
     184             : 
     185           4 :   qs = db_plugin->update_wirewatch_progress (db_plugin->cls,
     186           4 :                                              w->instance_id,
     187             :                                              w->payto_uri,
     188             :                                              w->start_row);
     189           4 :   if (qs < 0)
     190             :   {
     191           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     192             :                 "Failed to persist wirewatch progress for %s/%s (%d)\n",
     193             :                 w->instance_id,
     194             :                 w->payto_uri.full_payto,
     195             :                 qs);
     196           0 :     GNUNET_SCHEDULER_shutdown ();
     197           0 :     global_ret = EXIT_FAILURE;
     198             :   }
     199           4 : }
     200             : 
     201             : 
     202             : /**
     203             :  * Free resources of @a w.
     204             :  *
     205             :  * @param w watch job to terminate
     206             :  */
     207             : static void
     208           2 : end_watch (struct Watch *w)
     209             : {
     210           2 :   if (NULL != w->task)
     211             :   {
     212           0 :     GNUNET_SCHEDULER_cancel (w->task);
     213           0 :     w->task = NULL;
     214             :   }
     215           2 :   if (NULL != w->hh)
     216             :   {
     217           0 :     TALER_MERCHANT_BANK_credit_history_cancel (w->hh);
     218           0 :     w->hh = NULL;
     219             :   }
     220           2 :   GNUNET_free (w->instance_id);
     221           2 :   GNUNET_free (w->payto_uri.full_payto);
     222           2 :   TALER_MERCHANT_BANK_auth_free (&w->ad);
     223           2 :   GNUNET_CONTAINER_DLL_remove (w_head,
     224             :                                w_tail,
     225             :                                w);
     226           2 :   GNUNET_free (w);
     227           2 : }
     228             : 
     229             : 
     230             : /**
     231             :  * We're being aborted with CTRL-C (or SIGTERM). Shut down.
     232             :  *
     233             :  * @param cls closure
     234             :  */
     235             : static void
     236           2 : shutdown_task (void *cls)
     237             : {
     238             :   (void) cls;
     239           2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     240             :               "Running shutdown\n");
     241           2 :   while (NULL != w_head)
     242             :   {
     243           0 :     struct Watch *w = w_head;
     244             : 
     245           0 :     save (w);
     246           0 :     end_watch (w);
     247             :   }
     248           2 :   if (NULL != eh)
     249             :   {
     250           2 :     db_plugin->event_listen_cancel (eh);
     251           2 :     eh = NULL;
     252             :   }
     253           2 :   TALER_MERCHANTDB_plugin_unload (db_plugin);
     254           2 :   db_plugin = NULL;
     255           2 :   cfg = NULL;
     256           2 :   if (NULL != ctx)
     257             :   {
     258           2 :     GNUNET_CURL_fini (ctx);
     259           2 :     ctx = NULL;
     260             :   }
     261           2 :   if (NULL != rc)
     262             :   {
     263           2 :     GNUNET_CURL_gnunet_rc_destroy (rc);
     264           2 :     rc = NULL;
     265             :   }
     266           2 : }
     267             : 
     268             : 
     269             : /**
     270             :  * Parse @a subject from wire transfer into @a wtid and @a exchange_url.
     271             :  *
     272             :  * @param subject wire transfer subject to parse;
     273             :  *        format is "$WTID $URL"
     274             :  * @param[out] wtid wire transfer ID to extract
     275             :  * @param[out] exchange_url set to exchange URL
     276             :  * @return #GNUNET_OK on success
     277             :  */
     278             : static enum GNUNET_GenericReturnValue
     279           4 : parse_subject (const char *subject,
     280             :                struct TALER_WireTransferIdentifierRawP *wtid,
     281             :                char **exchange_url)
     282             : {
     283             :   const char *space;
     284             : 
     285           4 :   space = strchr (subject, ' ');
     286           4 :   if (NULL == space)
     287           0 :     return GNUNET_NO;
     288           4 :   if (GNUNET_OK !=
     289           4 :       GNUNET_STRINGS_string_to_data (subject,
     290           4 :                                      space - subject,
     291             :                                      wtid,
     292             :                                      sizeof (*wtid)))
     293           0 :     return GNUNET_NO;
     294           4 :   space++;
     295           4 :   if (! TALER_url_valid_charset (space))
     296           0 :     return GNUNET_NO;
     297           4 :   if ( (0 != strncasecmp ("http://",
     298             :                           space,
     299           0 :                           strlen ("http://"))) &&
     300           0 :        (0 != strncasecmp ("https://",
     301             :                           space,
     302             :                           strlen ("https://"))) )
     303           0 :     return GNUNET_NO;
     304           4 :   *exchange_url = GNUNET_strdup (space);
     305           4 :   return GNUNET_OK;
     306             : }
     307             : 
     308             : 
     309             : /**
     310             :  * Run next iteration.
     311             :  *
     312             :  * @param cls a `struct Watch *`
     313             :  */
     314             : static void
     315             : do_work (void *cls);
     316             : 
     317             : 
     318             : /**
     319             :  * Callbacks of this type are used to serve the result of asking
     320             :  * the bank for the credit transaction history.
     321             :  *
     322             :  * @param cls a `struct Watch *`
     323             :  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
     324             :  *                    0 if the bank's reply is bogus (fails to follow the protocol),
     325             :  *                    #MHD_HTTP_NO_CONTENT if there are no more results; on success the
     326             :  *                    last callback is always of this status (even if `abs(num_results)` were
     327             :  *                    already returned).
     328             :  * @param ec detailed error code
     329             :  * @param serial_id monotonically increasing counter corresponding to the transaction
     330             :  * @param details details about the wire transfer
     331             :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
     332             :  */
     333             : static enum GNUNET_GenericReturnValue
     334           8 : credit_cb (
     335             :   void *cls,
     336             :   unsigned int http_status,
     337             :   enum TALER_ErrorCode ec,
     338             :   uint64_t serial_id,
     339             :   const struct TALER_MERCHANT_BANK_CreditDetails *details)
     340             : {
     341           8 :   struct Watch *w = cls;
     342             : 
     343           8 :   switch (http_status)
     344             :   {
     345           0 :   case 0:
     346           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     347             :                 "Invalid HTTP response (HTTP status: 0, %d) from bank\n",
     348             :                 ec);
     349           0 :     w->delay = GNUNET_TIME_STD_BACKOFF (w->delay);
     350           0 :     break;
     351           4 :   case MHD_HTTP_OK:
     352             :     {
     353             :       enum GNUNET_DB_QueryStatus qs;
     354             :       char *exchange_url;
     355             :       struct TALER_WireTransferIdentifierRawP wtid;
     356             : 
     357           4 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     358             :                   "Received wire transfer `%s' over %s\n",
     359             :                   details->wire_subject,
     360             :                   TALER_amount2s (&details->amount));
     361           4 :       w->found = true;
     362           4 :       if (GNUNET_OK !=
     363           4 :           parse_subject (details->wire_subject,
     364             :                          &wtid,
     365             :                          &exchange_url))
     366             :       {
     367           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     368             :                     "Skipping transfer %llu (%s): not from exchange\n",
     369             :                     (unsigned long long) serial_id,
     370             :                     details->wire_subject);
     371           0 :         w->start_row = serial_id;
     372           0 :         return GNUNET_OK;
     373             :       }
     374             :       /* FIXME-Performance-Optimization: consider grouping multiple inserts
     375             :          into one bigger transaction with just one notify. */
     376           4 :       qs = db_plugin->insert_transfer (db_plugin->cls,
     377           4 :                                        w->instance_id,
     378             :                                        exchange_url,
     379             :                                        &wtid,
     380             :                                        &details->amount,
     381             :                                        details->credit_account_uri,
     382             :                                        true /* confirmed */);
     383           4 :       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     384             :       {
     385             :         struct TALER_Amount total;
     386             :         struct TALER_Amount wfee;
     387             :         struct TALER_Amount eamount;
     388             :         struct GNUNET_TIME_Timestamp timestamp;
     389             :         bool have_esig;
     390             :         bool verified;
     391             : 
     392           2 :         qs = db_plugin->lookup_transfer (db_plugin->cls,
     393           2 :                                          w->instance_id,
     394             :                                          exchange_url,
     395             :                                          &wtid,
     396             :                                          &total,
     397             :                                          &wfee,
     398             :                                          &eamount,
     399             :                                          &timestamp,
     400             :                                          &have_esig,
     401             :                                          &verified);
     402           2 :         if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     403             :         {
     404           0 :           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     405             :                       "Inserting transfer for %s into database failed. Is the credit account %s configured correctly?\n",
     406             :                       w->instance_id,
     407             :                       details->credit_account_uri.full_payto);
     408             :         }
     409           2 :         if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     410             :         {
     411           2 :           if (0 !=
     412           2 :               TALER_amount_cmp (&total,
     413             :                                 &details->amount))
     414             :           {
     415           2 :             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     416             :                         "Inserting transfer for %s into database failed. An entry exists for a different transfer amount (%s)!\n",
     417             :                         w->instance_id,
     418             :                         TALER_amount2s (&total));
     419             :           }
     420             :           else
     421             :           {
     422           0 :             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     423             :                         "Inserting transfer for %s into database failed. An equivalent entry already exists.\n",
     424             :                         w->instance_id);
     425             :           }
     426             :         }
     427             :       }
     428           4 :       GNUNET_free (exchange_url);
     429           4 :       if (qs < 0)
     430             :       {
     431           0 :         GNUNET_break (0);
     432           0 :         GNUNET_SCHEDULER_shutdown ();
     433           0 :         w->hh = NULL;
     434           0 :         return GNUNET_SYSERR;
     435             :       }
     436             :       /* Success => reset back-off timer! */
     437           4 :       w->delay = GNUNET_TIME_UNIT_ZERO;
     438             :       {
     439           4 :         struct GNUNET_DB_EventHeaderP es = {
     440           4 :           .size = htons (sizeof (es)),
     441           4 :           .type = htons (TALER_DBEVENT_MERCHANT_WIRE_TRANSFER_CONFIRMED)
     442             :         };
     443             : 
     444           4 :         db_plugin->event_notify (db_plugin->cls,
     445             :                                  &es,
     446             :                                  NULL,
     447             :                                  0);
     448             :       }
     449             :     }
     450           4 :     w->start_row = serial_id;
     451           4 :     return GNUNET_OK;
     452           4 :   case MHD_HTTP_NO_CONTENT:
     453           4 :     save (w);
     454             :     /* Delay artificially if server returned before long-poll timeout */
     455           4 :     if (! w->found)
     456           2 :       w->delay = GNUNET_TIME_absolute_get_remaining (w->long_poll_timeout);
     457           4 :     break;
     458           0 :   case MHD_HTTP_NOT_FOUND:
     459             :     /* configuration likely wrong, wait at least 1 minute, backoff up to 15 minutes! */
     460           0 :     w->delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MINUTES,
     461             :                                          GNUNET_TIME_STD_BACKOFF (w->delay));
     462           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     463             :                 "Bank claims account is unknown, waiting for %s before trying again\n",
     464             :                 GNUNET_TIME_relative2s (w->delay,
     465             :                                         true));
     466           0 :     break;
     467           0 :   case MHD_HTTP_GATEWAY_TIMEOUT:
     468           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     469             :                 "Gateway timeout, adjusting long polling threshold\n");
     470             :     /* Limit new timeout at request delay */
     471             :     w->bank_timeout
     472           0 :       = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_duration (
     473             :                                     w->start_time),
     474             :                                   w->bank_timeout);
     475             :     /* set the timeout a bit earlier */
     476             :     w->bank_timeout
     477           0 :       = GNUNET_TIME_relative_subtract (w->bank_timeout,
     478             :                                        GNUNET_TIME_UNIT_SECONDS);
     479             :     /* do not allow it to go to zero */
     480             :     w->bank_timeout
     481           0 :       = GNUNET_TIME_relative_max (w->bank_timeout,
     482             :                                   GNUNET_TIME_UNIT_SECONDS);
     483           0 :     w->delay = GNUNET_TIME_STD_BACKOFF (w->delay);
     484           0 :     break;
     485           0 :   default:
     486             :     /* Something went wrong, try again, but with back-off */
     487           0 :     w->delay = GNUNET_TIME_STD_BACKOFF (w->delay);
     488           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     489             :                 "Unexpected HTTP status code %u(%d) from bank\n",
     490             :                 http_status,
     491             :                 ec);
     492           0 :     break;
     493             :   }
     494           4 :   w->hh = NULL;
     495           4 :   if (test_mode && (! w->found))
     496             :   {
     497           2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     498             :                 "No transactions found and in test mode. Ending watch!\n");
     499           2 :     end_watch (w);
     500           2 :     if (NULL == w_head)
     501           2 :       GNUNET_SCHEDULER_shutdown ();
     502           2 :     return GNUNET_OK;
     503             :   }
     504           2 :   w->task = GNUNET_SCHEDULER_add_delayed (w->delay,
     505             :                                           &do_work,
     506             :                                           w);
     507           2 :   return GNUNET_OK;
     508             : }
     509             : 
     510             : 
     511             : static void
     512           4 : do_work (void *cls)
     513             : {
     514           4 :   struct Watch *w = cls;
     515             : 
     516           4 :   w->task = NULL;
     517           4 :   w->found = false;
     518             :   w->long_poll_timeout
     519           4 :     = GNUNET_TIME_relative_to_absolute (w->bank_timeout);
     520             :   w->start_time
     521           4 :     = GNUNET_TIME_absolute_get ();
     522           4 :   w->hh = TALER_MERCHANT_BANK_credit_history (ctx,
     523           4 :                                               &w->ad,
     524             :                                               w->start_row,
     525             :                                               batch_size,
     526             :                                               test_mode
     527           4 :                                               ? GNUNET_TIME_UNIT_ZERO
     528             :                                               : w->bank_timeout,
     529             :                                               &credit_cb,
     530             :                                               w);
     531           4 :   if (NULL == w->hh)
     532             :   {
     533           0 :     GNUNET_break (0);
     534           0 :     GNUNET_SCHEDULER_shutdown ();
     535           0 :     return;
     536             :   }
     537             : }
     538             : 
     539             : 
     540             : /**
     541             :  * Function called with information about a accounts
     542             :  * the wirewatcher should monitor.
     543             :  *
     544             :  * @param cls closure (NULL)
     545             :  * @param instance instance that owns the account
     546             :  * @param payto_uri account URI
     547             :  * @param credit_facade_url URL for the credit facade
     548             :  * @param credit_facade_credentials account access credentials
     549             :  * @param last_serial last transaction serial (inclusive) we have seen from this account
     550             :  */
     551             : static void
     552           2 : start_watch (
     553             :   void *cls,
     554             :   const char *instance,
     555             :   struct TALER_FullPayto payto_uri,
     556             :   const char *credit_facade_url,
     557             :   const json_t *credit_facade_credentials,
     558             :   uint64_t last_serial)
     559             : {
     560           2 :   struct Watch *w = GNUNET_new (struct Watch);
     561             : 
     562             :   (void) cls;
     563           2 :   w->bank_timeout = BANK_TIMEOUT;
     564           2 :   if (GNUNET_OK !=
     565           2 :       TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
     566             :                                            credit_facade_url,
     567             :                                            &w->ad))
     568             :   {
     569           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     570             :                 "Failed to parse authentication data of `%s/%s'\n",
     571             :                 instance,
     572             :                 payto_uri.full_payto);
     573           0 :     GNUNET_free (w);
     574           0 :     GNUNET_SCHEDULER_shutdown ();
     575           0 :     global_ret = EXIT_NOTCONFIGURED;
     576           0 :     return;
     577             :   }
     578             : 
     579           2 :   GNUNET_CONTAINER_DLL_insert (w_head,
     580             :                                w_tail,
     581             :                                w);
     582           2 :   w->instance_id = GNUNET_strdup (instance);
     583           2 :   w->payto_uri.full_payto = GNUNET_strdup (payto_uri.full_payto);
     584           2 :   w->start_row = last_serial;
     585           2 :   w->task = GNUNET_SCHEDULER_add_now (&do_work,
     586             :                                       w);
     587             : }
     588             : 
     589             : 
     590             : /**
     591             :  * Function called on configuration change events received from Postgres.  We
     592             :  * shutdown (and systemd should restart us).
     593             :  *
     594             :  * @param cls closure (NULL)
     595             :  * @param extra additional event data provided
     596             :  * @param extra_size number of bytes in @a extra
     597             :  */
     598             : static void
     599           0 : config_changed (void *cls,
     600             :                 const void *extra,
     601             :                 size_t extra_size)
     602             : {
     603             :   (void) cls;
     604             :   (void) extra;
     605             :   (void) extra_size;
     606           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     607             :               "Configuration changed, %s\n",
     608             :               0 == persist_mode
     609             :               ? "restarting"
     610             :               : "reinitializing");
     611           0 :   config_changed_flag = true;
     612           0 :   GNUNET_SCHEDULER_shutdown ();
     613           0 : }
     614             : 
     615             : 
     616             : /**
     617             :  * First task.
     618             :  *
     619             :  * @param cls closure, NULL
     620             :  * @param args remaining command-line arguments
     621             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
     622             :  * @param c configuration
     623             :  */
     624             : static void
     625           2 : run (void *cls,
     626             :      char *const *args,
     627             :      const char *cfgfile,
     628             :      const struct GNUNET_CONFIGURATION_Handle *c)
     629             : {
     630             :   (void) args;
     631             :   (void) cfgfile;
     632             : 
     633           2 :   cfg = c;
     634           2 :   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
     635             :                                  NULL);
     636           2 :   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
     637             :                           &rc);
     638           2 :   rc = GNUNET_CURL_gnunet_rc_create (ctx);
     639           2 :   if (NULL == ctx)
     640             :   {
     641           0 :     GNUNET_break (0);
     642           0 :     GNUNET_SCHEDULER_shutdown ();
     643           0 :     global_ret = EXIT_FAILURE;
     644           0 :     return;
     645             :   }
     646           2 :   if (NULL ==
     647           2 :       (db_plugin = TALER_MERCHANTDB_plugin_load (cfg)))
     648             :   {
     649           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     650             :                 "Failed to initialize DB subsystem\n");
     651           0 :     GNUNET_SCHEDULER_shutdown ();
     652           0 :     global_ret = EXIT_NOTCONFIGURED;
     653           0 :     return;
     654             :   }
     655           2 :   if (GNUNET_OK !=
     656           2 :       db_plugin->connect (db_plugin->cls))
     657             :   {
     658           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     659             :                 "Failed to connect to database. Consider running taler-merchant-dbinit!\n");
     660           0 :     GNUNET_SCHEDULER_shutdown ();
     661           0 :     global_ret = EXIT_FAILURE;
     662           0 :     return;
     663             :   }
     664             :   {
     665           2 :     struct GNUNET_DB_EventHeaderP es = {
     666           2 :       .size = htons (sizeof (es)),
     667           2 :       .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
     668             :     };
     669             : 
     670           4 :     eh = db_plugin->event_listen (db_plugin->cls,
     671             :                                   &es,
     672           2 :                                   GNUNET_TIME_UNIT_FOREVER_REL,
     673             :                                   &config_changed,
     674             :                                   NULL);
     675             :   }
     676             :   {
     677             :     enum GNUNET_DB_QueryStatus qs;
     678             : 
     679           2 :     qs = db_plugin->select_wirewatch_accounts (db_plugin->cls,
     680             :                                                &start_watch,
     681             :                                                NULL);
     682           2 :     if (qs < 0)
     683             :     {
     684           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     685             :                   "Failed to obtain wirewatch accounts from database\n");
     686           0 :       GNUNET_SCHEDULER_shutdown ();
     687           0 :       global_ret = EXIT_NO_RESTART;
     688           0 :       return;
     689             :     }
     690           2 :     if ( (NULL == w_head) &&
     691           0 :          (GNUNET_YES == test_mode) )
     692             :     {
     693           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     694             :                   "No active wirewatch accounts in database and in test mode. Exiting.\n");
     695           0 :       GNUNET_SCHEDULER_shutdown ();
     696           0 :       global_ret = EXIT_SUCCESS;
     697           0 :       return;
     698             :     }
     699             :   }
     700             : }
     701             : 
     702             : 
     703             : /**
     704             :  * The main function of taler-merchant-wirewatch
     705             :  *
     706             :  * @param argc number of arguments from the command line
     707             :  * @param argv command line arguments
     708             :  * @return 0 ok, 1 on error
     709             :  */
     710             : int
     711           2 : main (int argc,
     712             :       char *const *argv)
     713             : {
     714           2 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
     715           2 :     GNUNET_GETOPT_option_flag ('p',
     716             :                                "persist",
     717             :                                "run in persist mode and do not exit on configuration changes",
     718             :                                &persist_mode),
     719           2 :     GNUNET_GETOPT_option_timetravel ('T',
     720             :                                      "timetravel"),
     721           2 :     GNUNET_GETOPT_option_flag ('t',
     722             :                                "test",
     723             :                                "run in test mode and exit when idle",
     724             :                                &test_mode),
     725           2 :     GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
     726             :     GNUNET_GETOPT_OPTION_END
     727             :   };
     728             :   enum GNUNET_GenericReturnValue ret;
     729             : 
     730             :   do {
     731           2 :     config_changed_flag = false;
     732           2 :     ret = GNUNET_PROGRAM_run (
     733             :       TALER_MERCHANT_project_data (),
     734             :       argc, argv,
     735             :       "taler-merchant-wirewatch",
     736             :       gettext_noop (
     737             :         "background process that watches for incoming wire transfers to the merchant bank account"),
     738             :       options,
     739             :       &run, NULL);
     740           2 :   } while ( (1 == persist_mode) &&
     741             :             config_changed_flag);
     742           2 :   if (GNUNET_SYSERR == ret)
     743           0 :     return EXIT_INVALIDARGUMENT;
     744           2 :   if (GNUNET_NO == ret)
     745           0 :     return EXIT_SUCCESS;
     746           2 :   return global_ret;
     747             : }
     748             : 
     749             : 
     750             : /* end of taler-exchange-wirewatch.c */

Generated by: LCOV version 1.16