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