LCOV - code coverage report
Current view: top level - backend - taler-merchant-wirewatch.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 124 202 61.4 %
Date: 2025-08-28 06:06:54 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2023, 2025 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           8 : save (struct Watch *w)
     182             : {
     183             :   enum GNUNET_DB_QueryStatus qs;
     184             : 
     185           8 :   qs = db_plugin->update_wirewatch_progress (db_plugin->cls,
     186           8 :                                              w->instance_id,
     187             :                                              w->payto_uri,
     188             :                                              w->start_row);
     189           8 :   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           8 : }
     200             : 
     201             : 
     202             : /**
     203             :  * Free resources of @a w.
     204             :  *
     205             :  * @param w watch job to terminate
     206             :  */
     207             : static void
     208           4 : end_watch (struct Watch *w)
     209             : {
     210           4 :   if (NULL != w->task)
     211             :   {
     212           0 :     GNUNET_SCHEDULER_cancel (w->task);
     213           0 :     w->task = NULL;
     214             :   }
     215           4 :   if (NULL != w->hh)
     216             :   {
     217           0 :     TALER_MERCHANT_BANK_credit_history_cancel (w->hh);
     218           0 :     w->hh = NULL;
     219             :   }
     220           4 :   GNUNET_free (w->instance_id);
     221           4 :   GNUNET_free (w->payto_uri.full_payto);
     222           4 :   TALER_MERCHANT_BANK_auth_free (&w->ad);
     223           4 :   GNUNET_CONTAINER_DLL_remove (w_head,
     224             :                                w_tail,
     225             :                                w);
     226           4 :   GNUNET_free (w);
     227           4 : }
     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           4 : shutdown_task (void *cls)
     237             : {
     238             :   (void) cls;
     239           4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     240             :               "Running shutdown\n");
     241           4 :   while (NULL != w_head)
     242             :   {
     243           0 :     struct Watch *w = w_head;
     244             : 
     245           0 :     save (w);
     246           0 :     end_watch (w);
     247             :   }
     248           4 :   if (NULL != eh)
     249             :   {
     250           4 :     db_plugin->event_listen_cancel (eh);
     251           4 :     eh = NULL;
     252             :   }
     253           4 :   TALER_MERCHANTDB_plugin_unload (db_plugin);
     254           4 :   db_plugin = NULL;
     255           4 :   cfg = NULL;
     256           4 :   if (NULL != ctx)
     257             :   {
     258           4 :     GNUNET_CURL_fini (ctx);
     259           4 :     ctx = NULL;
     260             :   }
     261           4 :   if (NULL != rc)
     262             :   {
     263           4 :     GNUNET_CURL_gnunet_rc_destroy (rc);
     264           4 :     rc = NULL;
     265             :   }
     266           4 : }
     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          12 : 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          12 :   struct Watch *w = cls;
     342             : 
     343          12 :   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             :                                        serial_id);
     383           4 :       GNUNET_free (exchange_url);
     384           4 :       if (qs < 0)
     385             :       {
     386           0 :         GNUNET_break (0);
     387           0 :         GNUNET_SCHEDULER_shutdown ();
     388           0 :         w->hh = NULL;
     389           0 :         return GNUNET_SYSERR;
     390             :       }
     391             :       /* Success => reset back-off timer! */
     392           4 :       w->delay = GNUNET_TIME_UNIT_ZERO;
     393             :       {
     394           4 :         struct GNUNET_DB_EventHeaderP es = {
     395           4 :           .size = htons (sizeof (es)),
     396           4 :           .type = htons (TALER_DBEVENT_MERCHANT_WIRE_TRANSFER_CONFIRMED)
     397             :         };
     398             : 
     399           4 :         db_plugin->event_notify (db_plugin->cls,
     400             :                                  &es,
     401             :                                  NULL,
     402             :                                  0);
     403             :       }
     404             :     }
     405           4 :     w->start_row = serial_id;
     406           4 :     return GNUNET_OK;
     407           8 :   case MHD_HTTP_NO_CONTENT:
     408           8 :     save (w);
     409             :     /* Delay artificially if server returned before long-poll timeout */
     410           8 :     if (! w->found)
     411           4 :       w->delay = GNUNET_TIME_absolute_get_remaining (w->long_poll_timeout);
     412           8 :     break;
     413           0 :   case MHD_HTTP_NOT_FOUND:
     414             :     /* configuration likely wrong, wait at least 1 minute, backoff up to 15 minutes! */
     415           0 :     w->delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MINUTES,
     416             :                                          GNUNET_TIME_STD_BACKOFF (w->delay));
     417           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     418             :                 "Bank claims account is unknown, waiting for %s before trying again\n",
     419             :                 GNUNET_TIME_relative2s (w->delay,
     420             :                                         true));
     421           0 :     break;
     422           0 :   case MHD_HTTP_GATEWAY_TIMEOUT:
     423           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     424             :                 "Gateway timeout, adjusting long polling threshold\n");
     425             :     /* Limit new timeout at request delay */
     426             :     w->bank_timeout
     427           0 :       = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_duration (
     428             :                                     w->start_time),
     429             :                                   w->bank_timeout);
     430             :     /* set the timeout a bit earlier */
     431             :     w->bank_timeout
     432           0 :       = GNUNET_TIME_relative_subtract (w->bank_timeout,
     433             :                                        GNUNET_TIME_UNIT_SECONDS);
     434             :     /* do not allow it to go to zero */
     435             :     w->bank_timeout
     436           0 :       = GNUNET_TIME_relative_max (w->bank_timeout,
     437             :                                   GNUNET_TIME_UNIT_SECONDS);
     438           0 :     w->delay = GNUNET_TIME_STD_BACKOFF (w->delay);
     439           0 :     break;
     440           0 :   default:
     441             :     /* Something went wrong, try again, but with back-off */
     442           0 :     w->delay = GNUNET_TIME_STD_BACKOFF (w->delay);
     443           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     444             :                 "Unexpected HTTP status code %u(%d) from bank\n",
     445             :                 http_status,
     446             :                 ec);
     447           0 :     break;
     448             :   }
     449           8 :   w->hh = NULL;
     450           8 :   if (test_mode && (! w->found))
     451             :   {
     452           4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     453             :                 "No transactions found and in test mode. Ending watch!\n");
     454           4 :     end_watch (w);
     455           4 :     if (NULL == w_head)
     456           4 :       GNUNET_SCHEDULER_shutdown ();
     457           4 :     return GNUNET_OK;
     458             :   }
     459           4 :   w->task = GNUNET_SCHEDULER_add_delayed (w->delay,
     460             :                                           &do_work,
     461             :                                           w);
     462           4 :   return GNUNET_OK;
     463             : }
     464             : 
     465             : 
     466             : static void
     467           8 : do_work (void *cls)
     468             : {
     469           8 :   struct Watch *w = cls;
     470             : 
     471           8 :   w->task = NULL;
     472           8 :   w->found = false;
     473             :   w->long_poll_timeout
     474           8 :     = GNUNET_TIME_relative_to_absolute (w->bank_timeout);
     475             :   w->start_time
     476           8 :     = GNUNET_TIME_absolute_get ();
     477           8 :   w->hh = TALER_MERCHANT_BANK_credit_history (ctx,
     478           8 :                                               &w->ad,
     479             :                                               w->start_row,
     480             :                                               batch_size,
     481             :                                               test_mode
     482           8 :                                               ? GNUNET_TIME_UNIT_ZERO
     483             :                                               : w->bank_timeout,
     484             :                                               &credit_cb,
     485             :                                               w);
     486           8 :   if (NULL == w->hh)
     487             :   {
     488           0 :     GNUNET_break (0);
     489           0 :     GNUNET_SCHEDULER_shutdown ();
     490           0 :     return;
     491             :   }
     492             : }
     493             : 
     494             : 
     495             : /**
     496             :  * Function called with information about a accounts
     497             :  * the wirewatcher should monitor.
     498             :  *
     499             :  * @param cls closure (NULL)
     500             :  * @param instance instance that owns the account
     501             :  * @param payto_uri account URI
     502             :  * @param credit_facade_url URL for the credit facade
     503             :  * @param credit_facade_credentials account access credentials
     504             :  * @param last_serial last transaction serial (inclusive) we have seen from this account
     505             :  */
     506             : static void
     507           4 : start_watch (
     508             :   void *cls,
     509             :   const char *instance,
     510             :   struct TALER_FullPayto payto_uri,
     511             :   const char *credit_facade_url,
     512             :   const json_t *credit_facade_credentials,
     513             :   uint64_t last_serial)
     514             : {
     515           4 :   struct Watch *w = GNUNET_new (struct Watch);
     516             : 
     517             :   (void) cls;
     518           4 :   w->bank_timeout = BANK_TIMEOUT;
     519           4 :   if (GNUNET_OK !=
     520           4 :       TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
     521             :                                            credit_facade_url,
     522             :                                            &w->ad))
     523             :   {
     524           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     525             :                 "Failed to parse authentication data of `%s/%s'\n",
     526             :                 instance,
     527             :                 payto_uri.full_payto);
     528           0 :     GNUNET_free (w);
     529           0 :     GNUNET_SCHEDULER_shutdown ();
     530           0 :     global_ret = EXIT_NOTCONFIGURED;
     531           0 :     return;
     532             :   }
     533             : 
     534           4 :   GNUNET_CONTAINER_DLL_insert (w_head,
     535             :                                w_tail,
     536             :                                w);
     537           4 :   w->instance_id = GNUNET_strdup (instance);
     538           4 :   w->payto_uri.full_payto = GNUNET_strdup (payto_uri.full_payto);
     539           4 :   w->start_row = last_serial;
     540           4 :   w->task = GNUNET_SCHEDULER_add_now (&do_work,
     541             :                                       w);
     542             : }
     543             : 
     544             : 
     545             : /**
     546             :  * Function called on configuration change events received from Postgres.  We
     547             :  * shutdown (and systemd should restart us).
     548             :  *
     549             :  * @param cls closure (NULL)
     550             :  * @param extra additional event data provided
     551             :  * @param extra_size number of bytes in @a extra
     552             :  */
     553             : static void
     554           0 : config_changed (void *cls,
     555             :                 const void *extra,
     556             :                 size_t extra_size)
     557             : {
     558             :   (void) cls;
     559             :   (void) extra;
     560             :   (void) extra_size;
     561           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     562             :               "Configuration changed, %s\n",
     563             :               0 == persist_mode
     564             :               ? "restarting"
     565             :               : "reinitializing");
     566           0 :   config_changed_flag = true;
     567           0 :   GNUNET_SCHEDULER_shutdown ();
     568           0 : }
     569             : 
     570             : 
     571             : /**
     572             :  * First task.
     573             :  *
     574             :  * @param cls closure, NULL
     575             :  * @param args remaining command-line arguments
     576             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
     577             :  * @param c configuration
     578             :  */
     579             : static void
     580           4 : run (void *cls,
     581             :      char *const *args,
     582             :      const char *cfgfile,
     583             :      const struct GNUNET_CONFIGURATION_Handle *c)
     584             : {
     585             :   (void) args;
     586             :   (void) cfgfile;
     587             : 
     588           4 :   cfg = c;
     589           4 :   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
     590             :                                  NULL);
     591           4 :   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
     592             :                           &rc);
     593           4 :   rc = GNUNET_CURL_gnunet_rc_create (ctx);
     594           4 :   if (NULL == ctx)
     595             :   {
     596           0 :     GNUNET_break (0);
     597           0 :     GNUNET_SCHEDULER_shutdown ();
     598           0 :     global_ret = EXIT_FAILURE;
     599           0 :     return;
     600             :   }
     601           4 :   if (NULL ==
     602           4 :       (db_plugin = TALER_MERCHANTDB_plugin_load (cfg)))
     603             :   {
     604           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     605             :                 "Failed to initialize DB subsystem\n");
     606           0 :     GNUNET_SCHEDULER_shutdown ();
     607           0 :     global_ret = EXIT_NOTCONFIGURED;
     608           0 :     return;
     609             :   }
     610           4 :   if (GNUNET_OK !=
     611           4 :       db_plugin->connect (db_plugin->cls))
     612             :   {
     613           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     614             :                 "Failed to connect to database. Consider running taler-merchant-dbinit!\n");
     615           0 :     GNUNET_SCHEDULER_shutdown ();
     616           0 :     global_ret = EXIT_FAILURE;
     617           0 :     return;
     618             :   }
     619             :   {
     620           4 :     struct GNUNET_DB_EventHeaderP es = {
     621           4 :       .size = htons (sizeof (es)),
     622           4 :       .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
     623             :     };
     624             : 
     625           8 :     eh = db_plugin->event_listen (db_plugin->cls,
     626             :                                   &es,
     627           4 :                                   GNUNET_TIME_UNIT_FOREVER_REL,
     628             :                                   &config_changed,
     629             :                                   NULL);
     630             :   }
     631             :   {
     632             :     enum GNUNET_DB_QueryStatus qs;
     633             : 
     634           4 :     qs = db_plugin->select_wirewatch_accounts (db_plugin->cls,
     635             :                                                &start_watch,
     636             :                                                NULL);
     637           4 :     if (qs < 0)
     638             :     {
     639           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     640             :                   "Failed to obtain wirewatch accounts from database\n");
     641           0 :       GNUNET_SCHEDULER_shutdown ();
     642           0 :       global_ret = EXIT_NO_RESTART;
     643           0 :       return;
     644             :     }
     645           4 :     if ( (NULL == w_head) &&
     646           0 :          (GNUNET_YES == test_mode) )
     647             :     {
     648           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     649             :                   "No active wirewatch accounts in database and in test mode. Exiting.\n");
     650           0 :       GNUNET_SCHEDULER_shutdown ();
     651           0 :       global_ret = EXIT_SUCCESS;
     652           0 :       return;
     653             :     }
     654             :   }
     655             : }
     656             : 
     657             : 
     658             : /**
     659             :  * The main function of taler-merchant-wirewatch
     660             :  *
     661             :  * @param argc number of arguments from the command line
     662             :  * @param argv command line arguments
     663             :  * @return 0 ok, 1 on error
     664             :  */
     665             : int
     666           4 : main (int argc,
     667             :       char *const *argv)
     668             : {
     669           4 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
     670           4 :     GNUNET_GETOPT_option_flag ('p',
     671             :                                "persist",
     672             :                                "run in persist mode and do not exit on configuration changes",
     673             :                                &persist_mode),
     674           4 :     GNUNET_GETOPT_option_timetravel ('T',
     675             :                                      "timetravel"),
     676           4 :     GNUNET_GETOPT_option_flag ('t',
     677             :                                "test",
     678             :                                "run in test mode and exit when idle",
     679             :                                &test_mode),
     680           4 :     GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
     681             :     GNUNET_GETOPT_OPTION_END
     682             :   };
     683             :   enum GNUNET_GenericReturnValue ret;
     684             : 
     685             :   do {
     686           4 :     config_changed_flag = false;
     687           4 :     ret = GNUNET_PROGRAM_run (
     688             :       TALER_MERCHANT_project_data (),
     689             :       argc, argv,
     690             :       "taler-merchant-wirewatch",
     691             :       gettext_noop (
     692             :         "background process that watches for incoming wire transfers to the merchant bank account"),
     693             :       options,
     694             :       &run, NULL);
     695           4 :   } while ( (1 == persist_mode) &&
     696             :             config_changed_flag);
     697           4 :   if (GNUNET_SYSERR == ret)
     698           0 :     return EXIT_INVALIDARGUMENT;
     699           4 :   if (GNUNET_NO == ret)
     700           0 :     return EXIT_SUCCESS;
     701           4 :   return global_ret;
     702             : }
     703             : 
     704             : 
     705             : /* end of taler-exchange-wirewatch.c */

Generated by: LCOV version 1.16