taler-merchant-report-generator.c (26914B)
1 /* 2 This file is part of TALER 3 (C) 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 /** 18 * @file src/backend/taler-merchant-report-generator.c 19 * @brief Service for fetching and transmitting merchant reports 20 * @author Christian Grothoff 21 */ 22 #include "platform.h" 23 #include <gnunet/gnunet_util_lib.h> 24 #include <gnunet/gnunet_db_lib.h> 25 #include <gnunet/gnunet_curl_lib.h> 26 #include <taler/taler_merchant_util.h> 27 #include <taler/taler_curl_lib.h> 28 #include <taler/taler_dbevents.h> 29 #include <taler/taler_error_codes.h> 30 #include "merchantdb_lib.h" 31 #include "merchantdb_lib.h" 32 #include "taler/taler_merchant_service.h" 33 #include <microhttpd.h> 34 #include <curl/curl.h> 35 #include "merchant-database/delete_report.h" 36 #include "merchant-database/lookup_reports_pending.h" 37 #include "merchant-database/update_report_status.h" 38 #include "merchant-database/set_instance.h" 39 #include "merchant-database/event_listen.h" 40 41 42 /** 43 * Information about an active reporting activity. 44 */ 45 struct ReportActivity 46 { 47 48 /** 49 * Kept in a DLL. 50 */ 51 struct ReportActivity *next; 52 53 /** 54 * Kept in a DLL. 55 */ 56 struct ReportActivity *prev; 57 58 /** 59 * Transmission program that is running. 60 */ 61 struct GNUNET_Process *proc; 62 63 /** 64 * Handle to wait for @e proc to terminate. 65 */ 66 struct GNUNET_ChildWaitHandle *cwh; 67 68 /** 69 * Minor context that holds body and headers. 70 */ 71 struct TALER_CURL_PostContext post_ctx; 72 73 /** 74 * CURL easy handle for the HTTP request. 75 */ 76 CURL *eh; 77 78 /** 79 * Job handle for the HTTP request. 80 */ 81 struct GNUNET_CURL_Job *job; 82 83 /** 84 * ID of the instance we are working on. 85 */ 86 char *instance_id; 87 88 /** 89 * URL where we request the report from. 90 */ 91 char *url; 92 93 /** 94 * Report program section. 95 */ 96 char *report_program_section; 97 98 /** 99 * Report description. 100 */ 101 char *report_description; 102 103 /** 104 * Target address for transmission. 105 */ 106 char *target_address; 107 108 /** 109 * MIME type of the report. 110 */ 111 char *mime_type; 112 113 /** 114 * Report we are working on. 115 */ 116 uint64_t report_id; 117 118 /** 119 * Next transmission time, already calculated. 120 */ 121 struct GNUNET_TIME_Absolute next_transmission; 122 123 /** 124 * HTTP response code. 125 */ 126 long response_code; 127 128 /** 129 * Set to true if this is a one-shot report. 130 */ 131 bool one_shot; 132 133 }; 134 135 136 /** 137 * Global return value. 138 */ 139 static int global_ret; 140 141 /** 142 * #GNUNET_YES if we are in test mode and should exit when idle. 143 */ 144 static int test_mode; 145 146 /** 147 * Base URL of the merchant backend. 148 */ 149 static char *base_url; 150 151 /** 152 * Our configuration. 153 */ 154 static const struct GNUNET_CONFIGURATION_Handle *cfg; 155 156 /** 157 * Database connection. 158 */ 159 static struct TALER_MERCHANTDB_PostgresContext *pg; 160 161 /** 162 * Event handler for database change notifications. 163 */ 164 static struct GNUNET_DB_EventHandler *eh; 165 166 /** 167 * Task for checking pending reports. 168 */ 169 static struct GNUNET_SCHEDULER_Task *report_task; 170 171 /** 172 * When is the current report_task scheduled to run? 173 */ 174 static struct GNUNET_TIME_Absolute report_task_due; 175 176 /** 177 * Context for CURL operations. 178 */ 179 static struct GNUNET_CURL_Context *curl_ctx; 180 181 /** 182 * Reschedule context for CURL. 183 */ 184 static struct GNUNET_CURL_RescheduleContext *curl_rc; 185 186 /** 187 * Head of DLL of active report activities. 188 */ 189 static struct ReportActivity *ra_head; 190 191 /** 192 * Tail of DLL of active report activities. 193 */ 194 static struct ReportActivity *ra_tail; 195 196 197 /** 198 * Free a report activity structure. 199 * 200 * @param[in] ra report activity to free 201 */ 202 static void 203 free_ra (struct ReportActivity *ra) 204 { 205 if (NULL != ra->cwh) 206 { 207 GNUNET_wait_child_cancel (ra->cwh); 208 ra->cwh = NULL; 209 } 210 if (NULL != ra->proc) 211 { 212 GNUNET_break (GNUNET_OK == 213 GNUNET_process_kill (ra->proc, 214 SIGKILL)); 215 GNUNET_break (GNUNET_OK == 216 GNUNET_process_wait (ra->proc, 217 true, 218 NULL, 219 NULL)); 220 GNUNET_process_destroy (ra->proc); 221 ra->proc = NULL; 222 } 223 TALER_curl_easy_post_finished (&ra->post_ctx); 224 if (NULL != ra->eh) 225 { 226 curl_easy_cleanup (ra->eh); 227 ra->eh = NULL; 228 } 229 if (NULL != ra->job) 230 { 231 GNUNET_CURL_job_cancel (ra->job); 232 ra->job = NULL; 233 } 234 GNUNET_CONTAINER_DLL_remove (ra_head, 235 ra_tail, 236 ra); 237 GNUNET_free (ra->instance_id); 238 GNUNET_free (ra->report_program_section); 239 GNUNET_free (ra->report_description); 240 GNUNET_free (ra->target_address); 241 GNUNET_free (ra->mime_type); 242 GNUNET_free (ra->url); 243 GNUNET_free (ra); 244 } 245 246 247 /** 248 * Check for pending reports and process them. 249 * 250 * @param cls closure (unused) 251 */ 252 static void 253 check_pending_reports (void *cls); 254 255 256 /** 257 * Finish transmission of a report and update database. 258 * 259 * @param[in] ra report activity to finish 260 * @param ec error code (#TALER_EC_NONE on success) 261 * @param error_details human-readable error details (NULL on success) 262 */ 263 static void 264 finish_transmission (struct ReportActivity *ra, 265 enum TALER_ErrorCode ec, 266 const char *error_details) 267 { 268 enum GNUNET_DB_QueryStatus qs; 269 struct GNUNET_TIME_Timestamp next_ts; 270 271 next_ts = GNUNET_TIME_absolute_to_timestamp (ra->next_transmission); 272 qs = TALER_MERCHANTDB_set_instance (pg, 273 ra->instance_id); 274 if (qs <= 0) 275 { 276 free_ra (ra); 277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 278 "Failed to set instance to update report status: %d\n", 279 qs); 280 global_ret = EXIT_FAILURE; 281 GNUNET_SCHEDULER_shutdown (); 282 return; 283 } 284 if ( (TALER_EC_NONE == ec) && 285 (ra->one_shot) ) 286 { 287 qs = TALER_MERCHANTDB_delete_report (pg, 288 ra->instance_id, 289 ra->report_id); 290 } 291 else 292 { 293 qs = TALER_MERCHANTDB_update_report_status (pg, 294 ra->instance_id, 295 ra->report_id, 296 next_ts, 297 ec, 298 error_details); 299 } 300 if (qs < 0) 301 { 302 free_ra (ra); 303 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 304 "Failed to update report status: %d\n", 305 qs); 306 global_ret = EXIT_FAILURE; 307 GNUNET_SCHEDULER_shutdown (); 308 return; 309 } 310 if ( (NULL == report_task) || 311 (GNUNET_TIME_absolute_cmp (report_task_due, 312 >, 313 ra->next_transmission)) ) 314 { 315 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 316 "Scheduling next report for %s\n", 317 GNUNET_TIME_absolute2s (ra->next_transmission)); 318 if (NULL != report_task) 319 GNUNET_SCHEDULER_cancel (report_task); 320 report_task_due = ra->next_transmission; 321 report_task = GNUNET_SCHEDULER_add_at (ra->next_transmission, 322 &check_pending_reports, 323 NULL); 324 } 325 free_ra (ra); 326 if (test_mode && 327 GNUNET_TIME_absolute_is_future (report_task_due) && 328 (NULL == ra_head)) 329 { 330 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 331 "Test mode, exiting because of going idle\n"); 332 GNUNET_SCHEDULER_shutdown (); 333 return; 334 } 335 } 336 337 338 /** 339 * Callback invoked when the child process terminates. 340 * 341 * @param cls closure, a `struct ReportActivity *` 342 * @param type type of the process 343 * @param exit_code exit code of the process 344 */ 345 static void 346 child_completed_cb (void *cls, 347 enum GNUNET_OS_ProcessStatusType type, 348 long unsigned int exit_code) 349 { 350 struct ReportActivity *ra = cls; 351 enum TALER_ErrorCode ec; 352 char *error_details = NULL; 353 354 ra->cwh = NULL; 355 GNUNET_process_destroy (ra->proc); 356 ra->proc = NULL; 357 if ( (GNUNET_OS_PROCESS_EXITED != type) || 358 (0 != exit_code) ) 359 { 360 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 361 "Report transmission program failed with status %d/%lu\n", 362 (int) type, 363 exit_code); 364 ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 365 GNUNET_asprintf (&error_details, 366 "Report transmission program exited with status %d/%lu", 367 (int) type, 368 exit_code); 369 } 370 else 371 { 372 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 373 "Report transmitted successfully\n"); 374 ec = TALER_EC_NONE; 375 } 376 finish_transmission (ra, 377 ec, 378 error_details); 379 GNUNET_free (error_details); 380 } 381 382 383 /** 384 * Transmit a report using the respective report program. 385 * 386 * @param[in,out] ra which report activity are we working on 387 * @param report_len length of @a report 388 * @param report binary report data to transmit 389 */ 390 static void 391 transmit_report (struct ReportActivity *ra, 392 size_t report_len, 393 const void *report) 394 { 395 const char *binary; 396 struct GNUNET_DISK_FileHandle *stdin_handle; 397 398 { 399 char *section; 400 401 GNUNET_asprintf (§ion, 402 "report-generator-%s", 403 ra->report_program_section); 404 if (GNUNET_OK != 405 GNUNET_CONFIGURATION_get_value_string (cfg, 406 section, 407 "BINARY", 408 (char **) &binary)) 409 { 410 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 411 section, 412 "BINARY"); 413 finish_transmission (ra, 414 TALER_EC_MERCHANT_GENERIC_REPORT_GENERATOR_UNCONFIGURED, 415 section); 416 GNUNET_free (section); 417 return; 418 } 419 GNUNET_free (section); 420 } 421 422 { 423 struct GNUNET_DISK_PipeHandle *stdin_pipe; 424 425 stdin_pipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); 426 if (NULL == stdin_pipe) 427 { 428 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 429 "pipe"); 430 finish_transmission (ra, 431 TALER_EC_GENERIC_OS_RESOURCE_ALLOCATION_FAILURE, 432 "pipe"); 433 return; 434 } 435 436 ra->proc = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR); 437 GNUNET_assert (GNUNET_OK == 438 GNUNET_process_set_options ( 439 ra->proc, 440 GNUNET_process_option_inherit_rpipe (stdin_pipe, 441 STDIN_FILENO))); 442 if (GNUNET_OK != 443 GNUNET_process_run_command_va (ra->proc, 444 binary, 445 binary, 446 "-d", 447 ra->report_description, 448 "-m", 449 ra->mime_type, 450 "-t", 451 ra->target_address, 452 NULL)) 453 { 454 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 455 "exec", 456 binary); 457 GNUNET_process_destroy (ra->proc); 458 ra->proc = NULL; 459 GNUNET_DISK_pipe_close (stdin_pipe); 460 finish_transmission (ra, 461 TALER_EC_MERCHANT_REPORT_GENERATOR_FAILED, 462 "Could not execute report generator binary"); 463 return; 464 } 465 466 /* Write report data to stdin of child process */ 467 stdin_handle = GNUNET_DISK_pipe_detach_end (stdin_pipe, 468 GNUNET_DISK_PIPE_END_WRITE); 469 GNUNET_DISK_pipe_close (stdin_pipe); 470 } 471 472 { 473 size_t off = 0; 474 475 while (off < report_len) 476 { 477 ssize_t wrote; 478 479 wrote = GNUNET_DISK_file_write (stdin_handle, 480 report + off, 481 report_len - off); 482 if (wrote <= 0) 483 break; 484 off += (size_t) wrote; 485 } 486 GNUNET_DISK_file_close (stdin_handle); 487 488 if (off != report_len) 489 { 490 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 491 "Failed to write report data to child process stdin\n"); 492 finish_transmission (ra, 493 TALER_EC_MERCHANT_REPORT_GENERATOR_FAILED, 494 "Failed to write to transmission program"); 495 return; 496 } 497 } 498 499 /* Wait for child to complete */ 500 ra->cwh = GNUNET_wait_child (ra->proc, 501 &child_completed_cb, 502 ra); 503 } 504 505 506 /** 507 * Callback invoked when CURL request completes. 508 * 509 * @param cls closure, a `struct ReportActivity *` 510 * @param response_code HTTP response code 511 * @param body http body of the response 512 * @param body_size number of bytes in @a body 513 */ 514 static void 515 curl_completed_cb (void *cls, 516 long response_code, 517 const void *body, 518 size_t body_size) 519 { 520 struct ReportActivity *ra = cls; 521 522 ra->job = NULL; 523 ra->response_code = response_code; 524 if (MHD_HTTP_OK != response_code) 525 { 526 char *error_details; 527 528 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 529 "Failed to fetch report data: HTTP %ld\n", 530 response_code); 531 GNUNET_asprintf (&error_details, 532 "HTTP request failed with status %ld from `%s'", 533 response_code, 534 ra->url); 535 finish_transmission (ra, 536 TALER_EC_MERCHANT_REPORT_FETCH_FAILED, 537 error_details); 538 GNUNET_free (error_details); 539 return; 540 } 541 transmit_report (ra, 542 body_size, 543 body); 544 } 545 546 547 /** 548 * Function to fetch data from @a data_source at @a instance_id 549 * and to send it to the @a target_address 550 * 551 * @param[in,out] ra which report activity are we working on 552 * @param mime_type mime type to request from @a data_source 553 * @param report_token token to get access to the report 554 */ 555 static void 556 fetch_and_transmit ( 557 struct ReportActivity *ra, 558 const char *mime_type, 559 const struct TALER_MERCHANT_ReportToken *report_token) 560 { 561 GNUNET_asprintf (&ra->url, 562 "%sreports/%llu", 563 base_url, 564 (unsigned long long) ra->report_id); 565 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 566 "Fetching report from %s\n", 567 ra->url); 568 ra->eh = curl_easy_init (); 569 if (NULL == ra->eh) 570 { 571 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 572 "Failed to initialize CURL handle\n"); 573 finish_transmission (ra, 574 TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE, 575 "curl_easy_init"); 576 return; 577 } 578 579 { 580 char *accept_header; 581 582 GNUNET_asprintf (&accept_header, 583 "Accept: %s", 584 mime_type); 585 ra->post_ctx.headers = curl_slist_append (ra->post_ctx.headers, 586 accept_header); 587 GNUNET_free (accept_header); 588 } 589 GNUNET_assert (CURLE_OK == 590 curl_easy_setopt (ra->eh, 591 CURLOPT_URL, 592 ra->url)); 593 { 594 json_t *req; 595 596 req = GNUNET_JSON_PACK ( 597 GNUNET_JSON_pack_data_auto ("report_token", 598 report_token)); 599 if (GNUNET_OK != 600 TALER_curl_easy_post (&ra->post_ctx, 601 ra->eh, 602 req)) 603 { 604 GNUNET_break (0); 605 json_decref (req); 606 finish_transmission (ra, 607 TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE, 608 "TALER_curl_easy_post"); 609 return; 610 } 611 json_decref (req); 612 } 613 ra->job = GNUNET_CURL_job_add_raw (curl_ctx, 614 ra->eh, 615 ra->post_ctx.headers, 616 &curl_completed_cb, 617 ra); 618 /* On both success and failure ownership of the easy handle has been 619 taken over by GNUNET_CURL_job_add_raw() (on failure it already calls 620 curl_easy_cleanup()), so we must drop our reference to avoid a 621 double-free in free_ra(). */ 622 ra->eh = NULL; 623 if (NULL == ra->job) 624 { 625 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 626 "Failed to start the curl job for report #%llu\n", 627 (unsigned long long) ra->report_id); 628 finish_transmission (ra, 629 TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE, 630 "GNUNET_CURL_job_add_raw"); 631 return; 632 } 633 } 634 635 636 /** 637 * Callback invoked for each pending report. 638 * 639 * @param cls closure 640 * @param instance_id name of the instance 641 * @param report_id serial number of the report 642 * @param report_program_section configuration section of program 643 * for report generation 644 * @param report_description text describing the report 645 * @param mime_type mime type to request 646 * @param report_token token to authorize access to the data source 647 * @param target_address where to send report data 648 * @param frequency report frequency 649 * @param frequency_shift how much to shift the report time from a 650 * multiple of the report @a frequency 651 * @param next_transmission when is the next transmission of this report 652 * due 653 * @param one_shot true if the report should be removed from the 654 * list after generation instead of being repeated 655 */ 656 static void 657 process_pending_report ( 658 void *cls, 659 const char *instance_id, 660 uint64_t report_id, 661 const char *report_program_section, 662 const char *report_description, 663 const char *mime_type, 664 const struct TALER_MERCHANT_ReportToken *report_token, 665 const char *target_address, 666 struct GNUNET_TIME_Relative frequency, 667 struct GNUNET_TIME_Relative frequency_shift, 668 struct GNUNET_TIME_Absolute next_transmission, 669 bool one_shot) 670 { 671 struct GNUNET_TIME_Absolute *next = cls; 672 struct ReportActivity *ra; 673 674 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 675 "Next report %llu is pending at %s\n", 676 (unsigned long long) report_id, 677 GNUNET_TIME_absolute2s (next_transmission)); 678 *next = next_transmission; 679 if (GNUNET_TIME_absolute_is_future (next_transmission)) 680 return; 681 *next = GNUNET_TIME_UNIT_ZERO_ABS; /* there might be more! */ 682 if ( (one_shot) || 683 (GNUNET_TIME_relative_is_zero (frequency)) ) 684 { 685 next_transmission = GNUNET_TIME_UNIT_FOREVER_ABS; 686 } 687 else 688 { 689 next_transmission = 690 GNUNET_TIME_absolute_add ( 691 GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (), 692 frequency), 693 GNUNET_TIME_relative_add (frequency, 694 frequency_shift)); 695 } 696 if (! GNUNET_TIME_absolute_is_future (next_transmission)) 697 { 698 /* frequency near-zero!? */ 699 GNUNET_break (0); 700 next_transmission = GNUNET_TIME_relative_to_absolute ( 701 GNUNET_TIME_UNIT_MINUTES); 702 } 703 ra = GNUNET_new (struct ReportActivity); 704 ra->instance_id = GNUNET_strdup (instance_id); 705 ra->report_id = report_id; 706 ra->next_transmission = next_transmission; 707 ra->report_program_section = GNUNET_strdup (report_program_section); 708 ra->report_description = GNUNET_strdup (report_description); 709 ra->target_address = GNUNET_strdup (target_address); 710 ra->mime_type = GNUNET_strdup (mime_type); 711 ra->one_shot = one_shot; 712 GNUNET_CONTAINER_DLL_insert (ra_head, 713 ra_tail, 714 ra); 715 fetch_and_transmit (ra, 716 mime_type, 717 report_token); 718 } 719 720 721 static void 722 check_pending_reports (void *cls) 723 { 724 enum GNUNET_DB_QueryStatus qs; 725 struct GNUNET_TIME_Absolute next; 726 727 (void) cls; 728 report_task = NULL; 729 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 730 "Checking for pending reports...\n"); 731 next = GNUNET_TIME_UNIT_FOREVER_ABS; 732 qs = TALER_MERCHANTDB_lookup_reports_pending (pg, 733 &process_pending_report, 734 &next); 735 if (qs < 0) 736 { 737 GNUNET_break (0); 738 global_ret = EXIT_FAILURE; 739 GNUNET_SCHEDULER_shutdown (); 740 return; 741 } 742 if (NULL != ra_head) 743 return; /* wait for completion */ 744 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 745 "Found %d reports pending, next at %s\n", 746 (int) qs, 747 GNUNET_TIME_absolute2s (next)); 748 GNUNET_assert (NULL == report_task); 749 if (test_mode && 750 GNUNET_TIME_absolute_is_future (next) && 751 (NULL == ra_head)) 752 { 753 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 754 "Test mode, existing because of going idle\n"); 755 GNUNET_SCHEDULER_shutdown (); 756 return; 757 } 758 report_task_due = next; 759 report_task = GNUNET_SCHEDULER_add_at (next, 760 &check_pending_reports, 761 NULL); 762 } 763 764 765 /** 766 * Callback invoked when a MERCHANT_REPORT_UPDATE event is received. 767 * 768 * @param cls closure (unused) 769 * @param extra additional event data (unused) 770 * @param extra_size size of @a extra 771 */ 772 static void 773 report_update_cb (void *cls, 774 const void *extra, 775 size_t extra_size) 776 { 777 (void) cls; 778 (void) extra; 779 (void) extra_size; 780 781 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 782 "Received MERCHANT_REPORT_UPDATE event\n"); 783 /* Cancel any pending check and schedule immediate execution */ 784 if (NULL != report_task) 785 GNUNET_SCHEDULER_cancel (report_task); 786 report_task_due = GNUNET_TIME_UNIT_ZERO_ABS; 787 report_task = GNUNET_SCHEDULER_add_now (&check_pending_reports, 788 NULL); 789 } 790 791 792 /** 793 * Shutdown the service cleanly. 794 * 795 * @param cls closure (unused) 796 */ 797 static void 798 do_shutdown (void *cls) 799 { 800 struct ReportActivity *ra; 801 802 (void) cls; 803 804 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 805 "Shutting down report generator service\n"); 806 807 while (NULL != (ra = ra_head)) 808 free_ra (ra); 809 810 if (NULL != report_task) 811 { 812 GNUNET_SCHEDULER_cancel (report_task); 813 report_task = NULL; 814 } 815 if (NULL != curl_rc) 816 { 817 GNUNET_CURL_gnunet_rc_destroy (curl_rc); 818 curl_rc = NULL; 819 } 820 if (NULL != curl_ctx) 821 { 822 GNUNET_CURL_fini (curl_ctx); 823 curl_ctx = NULL; 824 } 825 if (NULL != eh) 826 { 827 TALER_MERCHANTDB_event_listen_cancel (eh); 828 eh = NULL; 829 } 830 if (NULL != pg) 831 { 832 TALER_MERCHANTDB_disconnect (pg); 833 pg = NULL; 834 } 835 GNUNET_free (base_url); 836 base_url = NULL; 837 } 838 839 840 /** 841 * Main function for the report generator service. 842 * 843 * @param cls closure 844 * @param args remaining command-line arguments 845 * @param cfgfile name of the configuration file used 846 * @param config configuration 847 */ 848 static void 849 run (void *cls, 850 char *const *args, 851 const char *cfgfile, 852 const struct GNUNET_CONFIGURATION_Handle *config) 853 { 854 (void) cls; 855 (void) args; 856 (void) cfgfile; 857 858 cfg = config; 859 if (GNUNET_OK != 860 GNUNET_CONFIGURATION_get_value_string (cfg, 861 "merchant", 862 "BASE_URL", 863 &base_url)) 864 { 865 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 866 "merchant", 867 "BASE_URL"); 868 global_ret = EXIT_NOTCONFIGURED; 869 return; 870 } 871 if (! TALER_is_web_url (base_url)) 872 { 873 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 874 "merchant", 875 "BASE_URL", 876 "Not a Web URL"); 877 global_ret = EXIT_NOTCONFIGURED; 878 return; 879 } 880 881 /* Ensure base_url ends with '/' */ 882 if ('/' != base_url[strlen (base_url) - 1]) 883 { 884 char *tmp; 885 886 GNUNET_asprintf (&tmp, 887 "%s/", 888 base_url); 889 GNUNET_free (base_url); 890 base_url = tmp; 891 } 892 893 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 894 NULL); 895 896 curl_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 897 &curl_rc); 898 if (NULL == curl_ctx) 899 { 900 GNUNET_break (0); 901 global_ret = EXIT_FAILURE; 902 GNUNET_SCHEDULER_shutdown (); 903 return; 904 } 905 curl_rc = GNUNET_CURL_gnunet_rc_create (curl_ctx); 906 907 pg = TALER_MERCHANTDB_connect (cfg); 908 if (NULL == pg) 909 { 910 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 911 "Failed to connect to database. Consider running taler-merchant-dbconfig!\n"); 912 global_ret = EXIT_NOTINSTALLED; 913 GNUNET_SCHEDULER_shutdown (); 914 return; 915 } 916 { 917 struct GNUNET_DB_EventHeaderP ev = { 918 .size = htons (sizeof (ev)), 919 .type = htons (TALER_DBEVENT_MERCHANT_REPORT_UPDATE) 920 }; 921 922 eh = TALER_MERCHANTDB_event_listen (pg, 923 &ev, 924 GNUNET_TIME_UNIT_FOREVER_REL, 925 &report_update_cb, 926 NULL); 927 if (NULL == eh) 928 { 929 GNUNET_break (0); 930 global_ret = EXIT_FAILURE; 931 GNUNET_SCHEDULER_shutdown (); 932 return; 933 } 934 } 935 report_task = GNUNET_SCHEDULER_add_now (&check_pending_reports, 936 NULL); 937 } 938 939 940 /** 941 * The main function of the report generator service. 942 * 943 * @param argc number of arguments from the command line 944 * @param argv command line arguments 945 * @return 0 ok, 1 on error 946 */ 947 int 948 main (int argc, 949 char *const *argv) 950 { 951 struct GNUNET_GETOPT_CommandLineOption options[] = { 952 GNUNET_GETOPT_option_flag ('t', 953 "test", 954 "run in test mode and exit when idle", 955 &test_mode), 956 GNUNET_GETOPT_option_timetravel ('T', 957 "timetravel"), 958 GNUNET_GETOPT_option_version (VERSION), 959 GNUNET_GETOPT_OPTION_END 960 }; 961 enum GNUNET_GenericReturnValue ret; 962 963 ret = GNUNET_PROGRAM_run ( 964 TALER_MERCHANT_project_data (), 965 argc, argv, 966 "taler-merchant-report-generator", 967 "Fetch and transmit periodic merchant reports", 968 options, 969 &run, 970 NULL); 971 if (GNUNET_SYSERR == ret) 972 return EXIT_INVALIDARGUMENT; 973 if (GNUNET_NO == ret) 974 return EXIT_SUCCESS; 975 return global_ret; 976 } 977 978 979 /* end of taler-merchant-report-generator.c */