diff options
Diffstat (limited to 'src/backend/taler-merchant-wirewatch.c')
-rw-r--r-- | src/backend/taler-merchant-wirewatch.c | 326 |
1 files changed, 197 insertions, 129 deletions
diff --git a/src/backend/taler-merchant-wirewatch.c b/src/backend/taler-merchant-wirewatch.c index 7772befa..380bcc11 100644 --- a/src/backend/taler-merchant-wirewatch.c +++ b/src/backend/taler-merchant-wirewatch.c @@ -34,30 +34,83 @@ 30) /** - * The merchant's configuration. + * Information about a watch job. */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; +struct Watch +{ + /** + * Kept in a DLL. + */ + struct Watch *next; + + /** + * Kept in a DLL. + */ + struct Watch *prev; + + /** + * Next task to run, if any. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * For which instance are we importing bank transfers? + */ + char *instance_id; + + /** + * For which account are we importing bank transfers? + */ + char *payto_uri; + + /** + * Bank history request. + */ + struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh; + + /** + * Start row for the bank interaction. Exclusive. + */ + uint64_t start_row; + + /** + * Artificial delay to use between API calls. Used to + * throttle on failures. + */ + struct GNUNET_TIME_Relative delay; + + /** + * Login data for the bank. + */ + struct TALER_MERCHANT_BANK_AuthenticationData ad; + + /** + * Set to true if we found a transaction in the last iteration. + */ + bool found; + +}; + /** - * Our database plugin. + * Head of active watches. */ -static struct TALER_MERCHANTDB_Plugin *db_plugin; +static struct Watch *w_head; /** - * Login data for the bank. + * Tail of active watches. */ -static struct TALER_MERCHANT_BANK_AuthenticationData ad; +static struct Watch *w_tail; /** - * Next task to run, if any. + * The merchant's configuration. */ -static struct GNUNET_SCHEDULER_Task *task; +static const struct GNUNET_CONFIGURATION_Handle *cfg; /** - * Configuration section with authentication data. - * Set to default value, can be overridden via command-line. + * Our database plugin. */ -static char *section = "taler-merchant-wirewatch"; +static struct TALER_MERCHANTDB_Plugin *db_plugin; /** * Handle to the context for interacting with the bank. @@ -84,64 +137,61 @@ static unsigned int batch_size = 32; */ static int test_mode; -/** - * Bank history request. - */ -static struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh; - -/** - * Artificial delay to use between API calls. Used to - * throttle on failures. - */ -static struct GNUNET_TIME_Relative delay; - -/** - * For which instance are we importing bank transfers? - */ -static char *instance_id; - -/** - * Start row for the bank interaction. Exclusive. - */ -static uint64_t start_row; - -/** - * Set to true if we need to update instead of insert on the merchant_wirewatch table. - */ -static bool progress_update; - -/** - * Set to true if we found a transaction in the last iteration. - */ -static bool found; - /** * Save progress in DB. */ static void -save (void) +save (struct Watch *w) { enum GNUNET_DB_QueryStatus qs; - if (progress_update) - qs = db_plugin->update_wirewatch_progress (db_plugin->cls, - section, - start_row); - else - qs = db_plugin->insert_wirewatch_progress (db_plugin->cls, - section, - start_row); + qs = db_plugin->update_wirewatch_progress (db_plugin->cls, + w->instance_id, + w->payto_uri, + w->start_row); if (qs < 0) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to persist wirewatch progress (%d)\n", + "Failed to persist wirewatch progress for %s/%s (%d)\n", + w->instance_id, + w->payto_uri, qs); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_FAILURE; } } /** + * Free resources of @a w. + * + * @param w watch job to terminate + */ +static void +end_watch (struct Watch *w) +{ + if (NULL != w->task) + { + GNUNET_SCHEDULER_cancel (w->task); + w->task = NULL; + } + if (NULL != w->hh) + { + TALER_MERCHANT_BANK_credit_history_cancel (w->hh); + w->hh = NULL; + } + GNUNET_free (w->instance_id); + GNUNET_free (w->payto_uri); + TALER_MERCHANT_BANK_auth_free (&w->ad); + GNUNET_CONTAINER_DLL_remove (w_head, + w_tail, + w); + GNUNET_free (w); +} + + +/** * We're being aborted with CTRL-C (or SIGTERM). Shut down. * * @param cls closure @@ -152,17 +202,13 @@ shutdown_task (void *cls) (void) cls; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running shutdown\n"); - if (NULL != task) + while (NULL != w_head) { - GNUNET_SCHEDULER_cancel (task); - task = NULL; - } - if (NULL != hh) - { - TALER_MERCHANT_BANK_credit_history_cancel (hh); - hh = NULL; + struct Watch *w = w_head; + + save (w); + end_watch (w); } - save (); TALER_MERCHANTDB_plugin_unload (db_plugin); db_plugin = NULL; cfg = NULL; @@ -176,7 +222,6 @@ shutdown_task (void *cls) GNUNET_CURL_gnunet_rc_destroy (rc); rc = NULL; } - TALER_MERCHANT_BANK_auth_free (&ad); } @@ -223,7 +268,7 @@ parse_subject (const char *subject, /** * Run next iteration. * - * @param cls NULL + * @param cls a `struct Watch *` */ static void do_work (void *cls); @@ -233,7 +278,7 @@ do_work (void *cls); * Callbacks of this type are used to serve the result of asking * the bank for the credit transaction history. * - * @param cls closure + * @param cls a `struct Watch *` * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * 0 if the bank's reply is bogus (fails to follow the protocol), * #MHD_HTTP_NO_CONTENT if there are no more results; on success the @@ -252,13 +297,14 @@ credit_cb ( uint64_t serial_id, const struct TALER_MERCHANT_BANK_CreditDetails *details) { - (void) cls; + struct Watch *w = cls; + switch (http_status) { case 0: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid HTTP response from bank\n"); - delay = GNUNET_TIME_STD_BACKOFF (delay); + w->delay = GNUNET_TIME_STD_BACKOFF (w->delay); break; case MHD_HTTP_OK: { @@ -270,7 +316,7 @@ credit_cb ( "Received wire transfer `%s' over %s\n", details->wire_subject, TALER_amount2s (&details->amount)); - found = true; + w->found = true; if (GNUNET_OK != parse_subject (details->wire_subject, &wtid, @@ -280,11 +326,11 @@ credit_cb ( "Skipping transfer %llu (%s): not from exchange\n", (unsigned long long) serial_id, details->wire_subject); - start_row = serial_id; + w->start_row = serial_id; return GNUNET_OK; } qs = db_plugin->insert_transfer (db_plugin->cls, - instance_id, + w->instance_id, exchange_url, &wtid, &details->amount, @@ -294,7 +340,7 @@ credit_cb ( { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Inserting transfer for %s into database failed. Is the credit account %s configured correctly?\n", - instance_id, + w->instance_id, details->credit_account_uri); } GNUNET_free (exchange_url); @@ -305,31 +351,33 @@ credit_cb ( return GNUNET_SYSERR; } } - start_row = serial_id; + w->start_row = serial_id; return GNUNET_OK; case MHD_HTTP_NO_CONTENT: - save (); - delay = GNUNET_TIME_UNIT_ZERO; + save (w); + w->delay = GNUNET_TIME_UNIT_ZERO; break; default: - delay = GNUNET_TIME_STD_BACKOFF (delay); + w->delay = GNUNET_TIME_STD_BACKOFF (w->delay); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unexpected HTTP status code %u(%d) from bank\n", http_status, ec); break; } - hh = NULL; - if (test_mode && (! found)) + w->hh = NULL; + if (test_mode && (! w->found)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No transactions found and in test mode. Shutting down!\n"); - GNUNET_SCHEDULER_shutdown (); + "No transactions found and in test mode. Ending watch!\n"); + end_watch (w); + if (NULL == w_head) + GNUNET_SCHEDULER_shutdown (); return GNUNET_OK; } - task = GNUNET_SCHEDULER_add_delayed (delay, - &do_work, - NULL); + w->task = GNUNET_SCHEDULER_add_delayed (w->delay, + &do_work, + w); return GNUNET_OK; } @@ -337,19 +385,20 @@ credit_cb ( static void do_work (void *cls) { - (void) cls; - task = NULL; - found = false; - hh = TALER_MERCHANT_BANK_credit_history (ctx, - &ad, - start_row, - batch_size, - test_mode - ? GNUNET_TIME_UNIT_ZERO - : BANK_TIMEOUT, - &credit_cb, - NULL); - if (NULL == hh) + struct Watch *w = cls; + + w->task = NULL; + w->found = false; + w->hh = TALER_MERCHANT_BANK_credit_history (ctx, + &w->ad, + w->start_row, + batch_size, + test_mode + ? GNUNET_TIME_UNIT_ZERO + : BANK_TIMEOUT, + &credit_cb, + w); + if (NULL == w->hh) { GNUNET_break (0); GNUNET_SCHEDULER_shutdown (); @@ -359,6 +408,55 @@ do_work (void *cls) /** + * Function called with information about a accounts + * the wirewatcher should monitor. + * + * @param cls closure (NULL) + * @param instance instance that owns the account + * @param payto_uri account URI + * @param credit_facade_url URL for the credit facade + * @param credit_facade_credentials account access credentials + * @param last_serial last transaction serial (inclusive) we have seen from this account + */ +static void +start_watch ( + void *cls, + const char *instance, + const char *payto_uri, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + uint64_t last_serial) +{ + struct Watch *w = GNUNET_new (struct Watch); + + (void) cls; + if (GNUNET_OK != + TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials, + credit_facade_url, + &w->ad)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse authentication data of `%s/%s'\n", + instance, + payto_uri); + GNUNET_free (w); + GNUNET_SCHEDULER_shutdown (); + global_ret = 1; + return; + } + + GNUNET_CONTAINER_DLL_insert (w_head, + w_tail, + w); + w->instance_id = GNUNET_strdup (instance); + w->payto_uri = GNUNET_strdup (payto_uri); + w->start_row = last_serial; + w->task = GNUNET_SCHEDULER_add_now (&do_work, + w); +} + + +/** * First task. * * @param cls closure, NULL @@ -376,28 +474,6 @@ run (void *cls, (void) cfgfile; cfg = c; - if (GNUNET_OK != - TALER_MERCHANT_BANK_auth_parse_cfg (cfg, - section, - &ad)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse authentication data in `%s'\n", - section); - return; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "INSTANCE", - &instance_id)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "INSTANCE"); - TALER_MERCHANT_BANK_auth_free (&ad); - return; - } GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, @@ -425,24 +501,21 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + // FIXME: also add notification job! { enum GNUNET_DB_QueryStatus qs; - qs = db_plugin->select_wirewatch_progress (db_plugin->cls, - section, - &start_row); + qs = db_plugin->select_wirewatch_accounts (db_plugin->cls, + &start_watch, + NULL); if (qs < 0) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to obtain wirewatch progress from database\n"); + "Failed to obtain wirewatch accounts from database\n"); GNUNET_SCHEDULER_shutdown (); return; } - progress_update = (0 != qs); } - GNUNET_assert (NULL == task); - task = GNUNET_SCHEDULER_add_now (&do_work, - NULL); } @@ -458,11 +531,6 @@ main (int argc, char *const *argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_string ('s', - "section", - "SECTION", - "configuration section to use for bank authentication data", - §ion), GNUNET_GETOPT_option_flag ('t', "test", "run in test mode and exit when idle", |